diff --git a/utoipa-gen/src/component/features.rs b/utoipa-gen/src/component/features.rs index 0d895c1c..67297a73 100644 --- a/utoipa-gen/src/component/features.rs +++ b/utoipa-gen/src/component/features.rs @@ -43,29 +43,42 @@ where }) } -pub trait Name { - fn get_name() -> &'static str +pub trait FeatureLike: Parse { + fn get_name() -> std::borrow::Cow<'static, str> where Self: Sized; } -macro_rules! name { - ( $ident:ident = $name:literal ) => { - impl $crate::features::Name for $ident { - fn get_name() -> &'static str { - $name +macro_rules! impl_feature { + ( $( $name:literal => )? $( #[$meta:meta] )* $vis:vis $key:ident $ty:ident $( $tt:tt )* ) => { + $( #[$meta] )* + $vis $key $ty $( $tt )* + + impl $crate::features::FeatureLike for $ty { + fn get_name() -> std::borrow::Cow<'static, str> { + impl_feature!( @name $ty name: $( $name )* ) } } - impl std::fmt::Display for $ident { + impl std::fmt::Display for $ty { fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result { - let name = ::get_name(); - write!(f, "{name}") + let name = ::get_name(); + write!(f, "{name}", name = name.as_ref()) } } }; + ( @name $ty:ident name: $name:literal ) => { + std::borrow::Cow::Borrowed($name) + }; + ( @name $ty:ident name: ) => { + { + let snake = $crate::component::serde::RenameRule::Snake; + let renamed = snake.rename_variant(stringify!($ty)); + std::borrow::Cow::Owned(renamed) + } + }; } -use name; +use impl_feature; /// Define whether [`Feature`] variant is validatable or not pub trait Validatable { @@ -243,7 +256,7 @@ impl ToTokensDiagnostics for Feature { return Err(Diagnostics::new("As does not support `ToTokens`")) } Feature::Required(required) => { - let name = ::get_name(); + let name = ::get_name(); quote! { .#name(#required) } } }; @@ -415,7 +428,7 @@ macro_rules! parse_features { ($ident:ident as $( $feature:path ),*) => { { fn parse(input: syn::parse::ParseStream) -> syn::Result> { - let names = [$( ::get_name(), )* ]; + let names = [$( ::get_name(), )* ]; let mut features = Vec::::new(); let attributes = names.join(", "); @@ -431,7 +444,7 @@ macro_rules! parse_features { let name = &*ident.to_string(); $( - if name == ::get_name() { + if name == ::get_name() { features.push(<$feature as crate::component::features::Parse>::parse(input, ident)?.into()); if !input.is_empty() { input.parse::()?; @@ -440,7 +453,7 @@ macro_rules! parse_features { } )* - if !names.contains(&name) { + if !names.contains(&std::borrow::Cow::Borrowed(name)) { return Err(syn::Error::new(ident.span(), format!("unexpected attribute: {name}, expected any of: {attributes}"))) } } diff --git a/utoipa-gen/src/component/features/attributes.rs b/utoipa-gen/src/component/features/attributes.rs index 4648a99a..1346f897 100644 --- a/utoipa-gen/src/component/features/attributes.rs +++ b/utoipa-gen/src/component/features/attributes.rs @@ -12,12 +12,14 @@ use crate::path::parameter::{self, ParameterStyle}; use crate::schema_type::SchemaFormat; use crate::{parse_utils, AnyValue, Array, Diagnostics}; -use super::{name, Feature, Parse}; +use super::{impl_feature, Feature, Parse}; use quote::quote; -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Default(pub(crate) Option); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Default(pub(crate) Option); +} impl Default { pub fn new_default_trait(struct_ident: Ident, field_ident: syn::Member) -> Self { @@ -50,11 +52,11 @@ impl From for Feature { } } -name!(Default = "default"); - -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Example(AnyValue); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Example(AnyValue); +} impl Parse for Example { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result { @@ -74,11 +76,11 @@ impl From for Feature { } } -name!(Example = "example"); - -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Examples(Vec); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Examples(Vec); +} impl Parse for Examples { fn parse(input: ParseStream, _: Ident) -> syn::Result @@ -114,11 +116,11 @@ impl From for Feature { } } -name!(Examples = "examples"); - -#[derive(Default, Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct XmlAttr(schema::xml::XmlAttr); +impl_feature! {"xml" => + #[derive(Default, Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct XmlAttr(schema::xml::XmlAttr); +} impl XmlAttr { /// Split [`XmlAttr`] for [`GenericType::Vec`] returning tuple of [`XmlAttr`]s where first @@ -176,11 +178,11 @@ impl From for Feature { } } -name!(XmlAttr = "xml"); - -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Format(SchemaFormat<'static>); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Format(SchemaFormat<'static>); +} impl Parse for Format { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result { @@ -200,11 +202,11 @@ impl From for Feature { } } -name!(Format = "format"); - -#[derive(Clone, Copy)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct WriteOnly(bool); +impl_feature! { + #[derive(Clone, Copy)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct WriteOnly(bool); +} impl Parse for WriteOnly { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result { @@ -224,11 +226,11 @@ impl From for Feature { } } -name!(WriteOnly = "write_only"); - -#[derive(Clone, Copy)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct ReadOnly(bool); +impl_feature! { + #[derive(Clone, Copy)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct ReadOnly(bool); +} impl Parse for ReadOnly { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result { @@ -248,11 +250,11 @@ impl From for Feature { } } -name!(ReadOnly = "read_only"); - -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Title(String); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Title(String); +} impl Parse for Title { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result { @@ -272,11 +274,11 @@ impl From for Feature { } } -name!(Title = "title"); - -#[derive(Clone, Copy)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Nullable(bool); +impl_feature! { + #[derive(Clone, Copy)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Nullable(bool); +} impl Nullable { pub fn new() -> Self { @@ -314,11 +316,11 @@ impl From<Nullable> for Feature { } } -name!(Nullable = "nullable"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct Rename(String); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct Rename(String); +} impl Rename { pub fn into_value(self) -> String { @@ -344,12 +346,12 @@ impl From<Rename> for Feature { } } -name!(Rename = "rename"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct RenameAll(RenameRule); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct RenameAll(RenameRule); +} impl RenameAll { pub fn as_rename_rule(&self) -> &RenameRule { &self.0 @@ -374,11 +376,11 @@ impl From<RenameAll> for Feature { } } -name!(RenameAll = "rename_all"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct Style(ParameterStyle); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct Style(ParameterStyle); +} impl From<ParameterStyle> for Style { fn from(style: ParameterStyle) -> Self { @@ -404,11 +406,11 @@ impl From<Style> for Feature { } } -name!(Style = "style"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct ParameterIn(parameter::ParameterIn); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct ParameterIn(parameter::ParameterIn); +} impl Parse for ParameterIn { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> { @@ -428,11 +430,11 @@ impl From<ParameterIn> for Feature { } } -name!(ParameterIn = "parameter_in"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct AllowReserved(bool); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct AllowReserved(bool); +} impl Parse for AllowReserved { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> { @@ -452,11 +454,11 @@ impl From<AllowReserved> for Feature { } } -name!(AllowReserved = "allow_reserved"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct Explode(bool); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct Explode(bool); +} impl Parse for Explode { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> { @@ -476,11 +478,11 @@ impl From<Explode> for Feature { } } -name!(Explode = "explode"); - -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct ValueType(syn::Type); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct ValueType(syn::Type); +} impl ValueType { /// Create [`TypeTree`] from current [`syn::Type`]. @@ -501,11 +503,11 @@ impl From<ValueType> for Feature { } } -name!(ValueType = "value_type"); - -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Inline(pub(super) bool); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Inline(pub(super) bool); +} impl Parse for Inline { fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> { @@ -525,12 +527,12 @@ impl From<Inline> for Feature { } } -name!(Inline = "inline"); - -/// Specify names of unnamed fields with `names(...) attribute for `IntoParams` derive. -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct IntoParamsNames(Vec<String>); +impl_feature! {"names" => + /// Specify names of unnamed fields with `names(...) attribute for `IntoParams` derive. + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct IntoParamsNames(Vec<String>); +} impl IntoParamsNames { pub fn into_values(self) -> Vec<String> { @@ -555,11 +557,11 @@ impl From<IntoParamsNames> for Feature { } } -name!(IntoParamsNames = "names"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct SchemaWith(TypePath); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct SchemaWith(TypePath); +} impl Parse for SchemaWith { fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> { @@ -582,11 +584,11 @@ impl From<SchemaWith> for Feature { } } -name!(SchemaWith = "schema_with"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct Description(parse_utils::Value); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct Description(parse_utils::Value); +} impl Parse for Description { fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> @@ -615,15 +617,15 @@ impl From<Description> for Feature { } } -name!(Description = "description"); - -/// Deprecated feature parsed from macro attributes. -/// -/// This feature supports only syntax parsed from utoipa specific macro attributes, it does not -/// support Rust `#[deprecated]` attribute. -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct Deprecated(bool); +impl_feature! { + /// Deprecated feature parsed from macro attributes. + /// + /// This feature supports only syntax parsed from utoipa specific macro attributes, it does not + /// support Rust `#[deprecated]` attribute. + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct Deprecated(bool); +} impl Parse for Deprecated { fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> @@ -647,11 +649,11 @@ impl From<Deprecated> for Feature { } } -name!(Deprecated = "deprecated"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct As(pub TypePath); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct As(pub TypePath); +} impl Parse for As { fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> @@ -668,11 +670,11 @@ impl From<As> for Feature { } } -name!(As = "as"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct AdditionalProperties(bool); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct AdditionalProperties(bool); +} impl Parse for AdditionalProperties { fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> @@ -694,17 +696,17 @@ impl ToTokens for AdditionalProperties { } } -name!(AdditionalProperties = "additional_properties"); - impl From<AdditionalProperties> for Feature { fn from(value: AdditionalProperties) -> Self { Self::AdditionalProperties(value) } } -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct Required(pub bool); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct Required(pub bool); +} impl Required { pub fn is_true(&self) -> bool { @@ -749,11 +751,11 @@ impl From<Required> for Feature { } } -name!(Required = "required"); - -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct ContentEncoding(String); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct ContentEncoding(String); +} impl Parse for ContentEncoding { fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> @@ -770,17 +772,17 @@ impl ToTokens for ContentEncoding { } } -name!(ContentEncoding = "content_encoding"); - impl From<ContentEncoding> for Feature { fn from(value: ContentEncoding) -> Self { Self::ContentEncoding(value) } } -#[derive(Clone)] -#[cfg_attr(feature = "debug", derive(Debug))] -pub struct ContentMediaType(String); +impl_feature! { + #[derive(Clone)] + #[cfg_attr(feature = "debug", derive(Debug))] + pub struct ContentMediaType(String); +} impl Parse for ContentMediaType { fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> @@ -802,5 +804,3 @@ impl From<ContentMediaType> for Feature { Self::ContentMediaType(value) } } - -name!(ContentMediaType = "content_media_type"); diff --git a/utoipa-gen/src/component/features/validation.rs b/utoipa-gen/src/component/features/validation.rs index eedfbb32..a2e58626 100644 --- a/utoipa-gen/src/component/features/validation.rs +++ b/utoipa-gen/src/component/features/validation.rs @@ -6,11 +6,13 @@ use syn::LitStr; use crate::{parse_utils, Diagnostics}; use super::validators::Validator; -use super::{name, parse_integer, parse_number, Feature, Parse, Validate}; +use super::{impl_feature, parse_integer, parse_number, Feature, Parse, Validate}; -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct MultipleOf(pub(super) f64, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct MultipleOf(pub(super) f64, Ident); +} impl Validate for MultipleOf { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -40,11 +42,11 @@ impl From<MultipleOf> for Feature { } } -name!(MultipleOf = "multiple_of"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct Maximum(pub(super) f64, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct Maximum(pub(super) f64, Ident); +} impl Validate for Maximum { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -77,11 +79,11 @@ impl From<Maximum> for Feature { } } -name!(Maximum = "maximum"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct Minimum(f64, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct Minimum(f64, Ident); +} impl Minimum { pub fn new(value: f64, span: Span) -> Self { @@ -122,11 +124,11 @@ impl From<Minimum> for Feature { } } -name!(Minimum = "minimum"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct ExclusiveMaximum(f64, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct ExclusiveMaximum(f64, Ident); +} impl Validate for ExclusiveMaximum { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -159,11 +161,11 @@ impl From<ExclusiveMaximum> for Feature { } } -name!(ExclusiveMaximum = "exclusive_maximum"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct ExclusiveMinimum(f64, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct ExclusiveMinimum(f64, Ident); +} impl Validate for ExclusiveMinimum { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -196,11 +198,11 @@ impl From<ExclusiveMinimum> for Feature { } } -name!(ExclusiveMinimum = "exclusive_minimum"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct MaxLength(pub(super) usize, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct MaxLength(pub(super) usize, Ident); +} impl Validate for MaxLength { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -233,11 +235,11 @@ impl From<MaxLength> for Feature { } } -name!(MaxLength = "max_length"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct MinLength(pub(super) usize, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct MinLength(pub(super) usize, Ident); +} impl Validate for MinLength { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -270,11 +272,11 @@ impl From<MinLength> for Feature { } } -name!(MinLength = "min_length"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct Pattern(String, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct Pattern(String, Ident); +} impl Validate for Pattern { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -309,11 +311,11 @@ impl From<Pattern> for Feature { } } -name!(Pattern = "pattern"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct MaxItems(pub(super) usize, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct MaxItems(pub(super) usize, Ident); +} impl Validate for MaxItems { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -346,11 +348,11 @@ impl From<MaxItems> for Feature { } } -name!(MaxItems = "max_items"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct MinItems(pub(super) usize, Ident); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct MinItems(pub(super) usize, Ident); +} impl Validate for MinItems { fn validate(&self, validator: impl Validator) -> Option<Diagnostics> { @@ -383,11 +385,11 @@ impl From<MinItems> for Feature { } } -name!(MinItems = "min_items"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct MaxProperties(usize, ()); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct MaxProperties(usize, ()); +} impl Parse for MaxProperties { fn parse(input: ParseStream, _ident: Ident) -> syn::Result<Self> @@ -410,11 +412,11 @@ impl From<MaxProperties> for Feature { } } -name!(MaxProperties = "max_properties"); - -#[cfg_attr(feature = "debug", derive(Debug))] -#[derive(Clone)] -pub struct MinProperties(usize, ()); +impl_feature! { + #[cfg_attr(feature = "debug", derive(Debug))] + #[derive(Clone)] + pub struct MinProperties(usize, ()); +} impl Parse for MinProperties { fn parse(input: ParseStream, _ident: Ident) -> syn::Result<Self> @@ -436,5 +438,3 @@ impl From<MinProperties> for Feature { Feature::MinProperties(value) } } - -name!(MinProperties = "min_properties");