utoipa_gen/component/
features.rs

1use std::{fmt::Display, mem};
2
3use proc_macro2::{Ident, TokenStream};
4use quote::{quote, ToTokens};
5use syn::parse::ParseStream;
6
7use crate::{
8    as_tokens_or_diagnostics, schema_type::SchemaType, Diagnostics, OptionExt, ToTokensDiagnostics,
9};
10
11use self::validators::{AboveZeroF64, AboveZeroUsize, IsNumber, IsString, IsVec, ValidatorChain};
12
13use super::TypeTree;
14
15pub mod attributes;
16pub mod validation;
17pub mod validators;
18
19pub trait FeatureLike: Parse {
20    fn get_name() -> std::borrow::Cow<'static, str>
21    where
22        Self: Sized;
23}
24
25macro_rules! impl_feature {
26    ( $( $name:literal => )? $( #[$meta:meta] )* $vis:vis $key:ident $ty:ident $( $tt:tt )* ) => {
27        $( #[$meta] )*
28        $vis $key $ty $( $tt )*
29
30        impl $crate::features::FeatureLike for $ty {
31            fn get_name() -> std::borrow::Cow<'static, str> {
32                impl_feature!( @name $ty name: $( $name )* )
33            }
34        }
35
36        impl std::fmt::Display for $ty {
37            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
38                let name = <Self as $crate::features::FeatureLike>::get_name();
39                write!(f, "{name}", name = name.as_ref())
40            }
41        }
42    };
43    ( @name $ty:ident name: $name:literal ) => {
44        std::borrow::Cow::Borrowed($name)
45    };
46    ( @name $ty:ident name: ) => {
47        {
48            let snake = $crate::component::serde::RenameRule::Snake;
49            let renamed = snake.rename_variant(stringify!($ty));
50            std::borrow::Cow::Owned(renamed)
51        }
52    };
53}
54use impl_feature;
55
56/// Define whether [`Feature`] variant is validatable or not
57pub trait Validatable {
58    fn is_validatable(&self) -> bool {
59        false
60    }
61}
62
63pub trait Validate: Validatable {
64    /// Perform validation check against schema type.
65    fn validate(&self, validator: impl validators::Validator) -> Option<Diagnostics>;
66}
67
68pub trait Parse {
69    fn parse(input: ParseStream, attribute: Ident) -> syn::Result<Self>
70    where
71        Self: std::marker::Sized;
72}
73
74#[cfg_attr(feature = "debug", derive(Debug))]
75#[derive(Clone)]
76pub enum Feature {
77    Example(attributes::Example),
78    Examples(attributes::Examples),
79    Default(attributes::Default),
80    Inline(attributes::Inline),
81    XmlAttr(attributes::XmlAttr),
82    Format(attributes::Format),
83    ValueType(attributes::ValueType),
84    WriteOnly(attributes::WriteOnly),
85    ReadOnly(attributes::ReadOnly),
86    Title(attributes::Title),
87    Nullable(attributes::Nullable),
88    Rename(attributes::Rename),
89    RenameAll(attributes::RenameAll),
90    Style(attributes::Style),
91    AllowReserved(attributes::AllowReserved),
92    Explode(attributes::Explode),
93    ParameterIn(attributes::ParameterIn),
94    IntoParamsNames(attributes::IntoParamsNames),
95    SchemaWith(attributes::SchemaWith),
96    Description(attributes::Description),
97    Deprecated(attributes::Deprecated),
98    As(attributes::As),
99    AdditionalProperties(attributes::AdditionalProperties),
100    Required(attributes::Required),
101    ContentEncoding(attributes::ContentEncoding),
102    ContentMediaType(attributes::ContentMediaType),
103    Discriminator(attributes::Discriminator),
104    Bound(attributes::Bound),
105    Ignore(attributes::Ignore),
106    NoRecursion(attributes::NoRecursion),
107    MultipleOf(validation::MultipleOf),
108    Maximum(validation::Maximum),
109    Minimum(validation::Minimum),
110    ExclusiveMaximum(validation::ExclusiveMaximum),
111    ExclusiveMinimum(validation::ExclusiveMinimum),
112    MaxLength(validation::MaxLength),
113    MinLength(validation::MinLength),
114    Pattern(validation::Pattern),
115    MaxItems(validation::MaxItems),
116    MinItems(validation::MinItems),
117    MaxProperties(validation::MaxProperties),
118    MinProperties(validation::MinProperties),
119    Extensions(attributes::Extensions),
120}
121
122impl Feature {
123    pub fn validate(&self, schema_type: &SchemaType, type_tree: &TypeTree) -> Option<Diagnostics> {
124        match self {
125            Feature::MultipleOf(multiple_of) => multiple_of.validate(
126                ValidatorChain::new(&IsNumber(schema_type)).next(&AboveZeroF64(&multiple_of.0)),
127            ),
128            Feature::Maximum(maximum) => maximum.validate(IsNumber(schema_type)),
129            Feature::Minimum(minimum) => minimum.validate(IsNumber(schema_type)),
130            Feature::ExclusiveMaximum(exclusive_maximum) => {
131                exclusive_maximum.validate(IsNumber(schema_type))
132            }
133            Feature::ExclusiveMinimum(exclusive_minimum) => {
134                exclusive_minimum.validate(IsNumber(schema_type))
135            }
136            Feature::MaxLength(max_length) => max_length.validate(
137                ValidatorChain::new(&IsString(schema_type)).next(&AboveZeroUsize(&max_length.0)),
138            ),
139            Feature::MinLength(min_length) => min_length.validate(
140                ValidatorChain::new(&IsString(schema_type)).next(&AboveZeroUsize(&min_length.0)),
141            ),
142            Feature::Pattern(pattern) => pattern.validate(IsString(schema_type)),
143            Feature::MaxItems(max_items) => max_items.validate(
144                ValidatorChain::new(&AboveZeroUsize(&max_items.0)).next(&IsVec(type_tree)),
145            ),
146            Feature::MinItems(min_items) => min_items.validate(
147                ValidatorChain::new(&AboveZeroUsize(&min_items.0)).next(&IsVec(type_tree)),
148            ),
149            unsupported => {
150                const SUPPORTED_VARIANTS: [&str; 10] = [
151                    "multiple_of",
152                    "maximum",
153                    "minimum",
154                    "exclusive_maximum",
155                    "exclusive_minimum",
156                    "max_length",
157                    "min_length",
158                    "pattern",
159                    "max_items",
160                    "min_items",
161                ];
162                panic!(
163                    "Unsupported variant: `{unsupported}` for Validate::validate, expected one of: {variants}",
164                    variants = SUPPORTED_VARIANTS.join(", ")
165                )
166            }
167        }
168    }
169}
170
171impl ToTokensDiagnostics for Feature {
172    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) -> Result<(), Diagnostics> {
173        let feature = match &self {
174            Feature::Default(default) => quote! { .default(#default) },
175            Feature::Example(example) => quote! { .example(Some(#example)) },
176            Feature::Examples(examples) => quote! { .examples(#examples) },
177            Feature::XmlAttr(xml) => quote! { .xml(Some(#xml)) },
178            Feature::Format(format) => quote! { .format(Some(#format)) },
179            Feature::WriteOnly(write_only) => quote! { .write_only(Some(#write_only)) },
180            Feature::ReadOnly(read_only) => quote! { .read_only(Some(#read_only)) },
181            Feature::Title(title) => quote! { .title(Some(#title)) },
182            Feature::Nullable(_nullable) => return Err(Diagnostics::new("Nullable does not support `ToTokens`")),
183            Feature::Rename(rename) => rename.to_token_stream(),
184            Feature::Style(style) => quote! { .style(Some(#style)) },
185            Feature::ParameterIn(parameter_in) => quote! { .parameter_in(#parameter_in) },
186            Feature::MultipleOf(multiple_of) => quote! { .multiple_of(Some(#multiple_of)) },
187            Feature::AllowReserved(allow_reserved) => {
188                quote! { .allow_reserved(Some(#allow_reserved)) }
189            }
190            Feature::Explode(explode) => quote! { .explode(Some(#explode)) },
191            Feature::Maximum(maximum) => quote! { .maximum(Some(#maximum)) },
192            Feature::Minimum(minimum) => quote! { .minimum(Some(#minimum)) },
193            Feature::ExclusiveMaximum(exclusive_maximum) => {
194                quote! { .exclusive_maximum(Some(#exclusive_maximum)) }
195            }
196            Feature::ExclusiveMinimum(exclusive_minimum) => {
197                quote! { .exclusive_minimum(Some(#exclusive_minimum)) }
198            }
199            Feature::MaxLength(max_length) => quote! { .max_length(Some(#max_length)) },
200            Feature::MinLength(min_length) => quote! { .min_length(Some(#min_length)) },
201            Feature::Pattern(pattern) => quote! { .pattern(Some(#pattern)) },
202            Feature::MaxItems(max_items) => quote! { .max_items(Some(#max_items)) },
203            Feature::MinItems(min_items) => quote! { .min_items(Some(#min_items)) },
204            Feature::MaxProperties(max_properties) => {
205                quote! { .max_properties(Some(#max_properties)) }
206            }
207            Feature::MinProperties(min_properties) => {
208                quote! { .max_properties(Some(#min_properties)) }
209            }
210            Feature::SchemaWith(schema_with) => schema_with.to_token_stream(),
211            Feature::Description(description) => quote! { .description(Some(#description)) },
212            Feature::Deprecated(deprecated) => quote! { .deprecated(Some(#deprecated)) },
213            Feature::AdditionalProperties(additional_properties) => {
214                quote! { .additional_properties(Some(#additional_properties)) }
215            }
216            Feature::ContentEncoding(content_encoding) => quote! { .content_encoding(#content_encoding) },
217            Feature::ContentMediaType(content_media_type) => quote! { .content_media_type(#content_media_type) },
218            Feature::Discriminator(discriminator) => quote! { .discriminator(Some(#discriminator)) },
219            Feature::Bound(_) => {
220                // specially handled on generating impl blocks.
221                TokenStream::new()
222            }
223            Feature::RenameAll(_) => {
224                return Err(Diagnostics::new("RenameAll feature does not support `ToTokens`"))
225            }
226            Feature::ValueType(_) => {
227                return Err(Diagnostics::new("ValueType feature does not support `ToTokens`")
228                    .help("ValueType is supposed to be used with `TypeTree` in same manner as a resolved struct/field type."))
229            }
230            Feature::Inline(_) => {
231                // inline feature is ignored by `ToTokens`
232                TokenStream::new()
233            }
234            Feature::NoRecursion(_) => return Err(Diagnostics::new("NoRecursion does not support `ToTokens`")),
235            Feature::IntoParamsNames(_) => {
236                return Err(Diagnostics::new("Names feature does not support `ToTokens`")
237                    .help("Names is only used with IntoParams to artificially give names for unnamed struct type `IntoParams`."))
238            }
239            Feature::As(_) => {
240                return Err(Diagnostics::new("As does not support `ToTokens`"))
241            }
242            Feature::Required(required) => {
243                let name = <attributes::Required as FeatureLike>::get_name();
244                quote! { .#name(#required) }
245            }
246            Feature::Ignore(_) => return Err(Diagnostics::new("Ignore does not support `ToTokens`")),
247            Feature::Extensions(extensions) => quote! { .extensions(Some(#extensions)) },
248        };
249
250        tokens.extend(feature);
251
252        Ok(())
253    }
254}
255
256impl ToTokensDiagnostics for Option<Feature> {
257    fn to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostics> {
258        if let Some(this) = self {
259            this.to_tokens(tokens)
260        } else {
261            Ok(())
262        }
263    }
264}
265
266impl Display for Feature {
267    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
268        match self {
269            Feature::Default(default) => default.fmt(f),
270            Feature::Example(example) => example.fmt(f),
271            Feature::Examples(examples) => examples.fmt(f),
272            Feature::XmlAttr(xml) => xml.fmt(f),
273            Feature::Format(format) => format.fmt(f),
274            Feature::WriteOnly(write_only) => write_only.fmt(f),
275            Feature::ReadOnly(read_only) => read_only.fmt(f),
276            Feature::Title(title) => title.fmt(f),
277            Feature::Nullable(nullable) => nullable.fmt(f),
278            Feature::Rename(rename) => rename.fmt(f),
279            Feature::Style(style) => style.fmt(f),
280            Feature::ParameterIn(parameter_in) => parameter_in.fmt(f),
281            Feature::AllowReserved(allow_reserved) => allow_reserved.fmt(f),
282            Feature::Explode(explode) => explode.fmt(f),
283            Feature::RenameAll(rename_all) => rename_all.fmt(f),
284            Feature::ValueType(value_type) => value_type.fmt(f),
285            Feature::Inline(inline) => inline.fmt(f),
286            Feature::IntoParamsNames(names) => names.fmt(f),
287            Feature::MultipleOf(multiple_of) => multiple_of.fmt(f),
288            Feature::Maximum(maximum) => maximum.fmt(f),
289            Feature::Minimum(minimum) => minimum.fmt(f),
290            Feature::ExclusiveMaximum(exclusive_maximum) => exclusive_maximum.fmt(f),
291            Feature::ExclusiveMinimum(exclusive_minimum) => exclusive_minimum.fmt(f),
292            Feature::MaxLength(max_length) => max_length.fmt(f),
293            Feature::MinLength(min_length) => min_length.fmt(f),
294            Feature::Pattern(pattern) => pattern.fmt(f),
295            Feature::MaxItems(max_items) => max_items.fmt(f),
296            Feature::MinItems(min_items) => min_items.fmt(f),
297            Feature::MaxProperties(max_properties) => max_properties.fmt(f),
298            Feature::MinProperties(min_properties) => min_properties.fmt(f),
299            Feature::SchemaWith(schema_with) => schema_with.fmt(f),
300            Feature::Description(description) => description.fmt(f),
301            Feature::Deprecated(deprecated) => deprecated.fmt(f),
302            Feature::As(as_feature) => as_feature.fmt(f),
303            Feature::AdditionalProperties(additional_properties) => additional_properties.fmt(f),
304            Feature::Required(required) => required.fmt(f),
305            Feature::ContentEncoding(content_encoding) => content_encoding.fmt(f),
306            Feature::ContentMediaType(content_media_type) => content_media_type.fmt(f),
307            Feature::Discriminator(discriminator) => discriminator.fmt(f),
308            Feature::Bound(bound) => bound.fmt(f),
309            Feature::Ignore(ignore) => ignore.fmt(f),
310            Feature::NoRecursion(no_recursion) => no_recursion.fmt(f),
311            Feature::Extensions(extensions) => extensions.fmt(f),
312        }
313    }
314}
315
316impl Validatable for Feature {
317    fn is_validatable(&self) -> bool {
318        match &self {
319            Feature::Default(default) => default.is_validatable(),
320            Feature::Example(example) => example.is_validatable(),
321            Feature::Examples(examples) => examples.is_validatable(),
322            Feature::XmlAttr(xml) => xml.is_validatable(),
323            Feature::Format(format) => format.is_validatable(),
324            Feature::WriteOnly(write_only) => write_only.is_validatable(),
325            Feature::ReadOnly(read_only) => read_only.is_validatable(),
326            Feature::Title(title) => title.is_validatable(),
327            Feature::Nullable(nullable) => nullable.is_validatable(),
328            Feature::Rename(rename) => rename.is_validatable(),
329            Feature::Style(style) => style.is_validatable(),
330            Feature::ParameterIn(parameter_in) => parameter_in.is_validatable(),
331            Feature::AllowReserved(allow_reserved) => allow_reserved.is_validatable(),
332            Feature::Explode(explode) => explode.is_validatable(),
333            Feature::RenameAll(rename_all) => rename_all.is_validatable(),
334            Feature::ValueType(value_type) => value_type.is_validatable(),
335            Feature::Inline(inline) => inline.is_validatable(),
336            Feature::IntoParamsNames(names) => names.is_validatable(),
337            Feature::MultipleOf(multiple_of) => multiple_of.is_validatable(),
338            Feature::Maximum(maximum) => maximum.is_validatable(),
339            Feature::Minimum(minimum) => minimum.is_validatable(),
340            Feature::ExclusiveMaximum(exclusive_maximum) => exclusive_maximum.is_validatable(),
341            Feature::ExclusiveMinimum(exclusive_minimum) => exclusive_minimum.is_validatable(),
342            Feature::MaxLength(max_length) => max_length.is_validatable(),
343            Feature::MinLength(min_length) => min_length.is_validatable(),
344            Feature::Pattern(pattern) => pattern.is_validatable(),
345            Feature::MaxItems(max_items) => max_items.is_validatable(),
346            Feature::MinItems(min_items) => min_items.is_validatable(),
347            Feature::MaxProperties(max_properties) => max_properties.is_validatable(),
348            Feature::MinProperties(min_properties) => min_properties.is_validatable(),
349            Feature::SchemaWith(schema_with) => schema_with.is_validatable(),
350            Feature::Description(description) => description.is_validatable(),
351            Feature::Deprecated(deprecated) => deprecated.is_validatable(),
352            Feature::As(as_feature) => as_feature.is_validatable(),
353            Feature::AdditionalProperties(additional_properties) => {
354                additional_properties.is_validatable()
355            }
356            Feature::Required(required) => required.is_validatable(),
357            Feature::ContentEncoding(content_encoding) => content_encoding.is_validatable(),
358            Feature::ContentMediaType(content_media_type) => content_media_type.is_validatable(),
359            Feature::Discriminator(discriminator) => discriminator.is_validatable(),
360            Feature::Bound(bound) => bound.is_validatable(),
361            Feature::Ignore(ignore) => ignore.is_validatable(),
362            Feature::NoRecursion(no_recursion) => no_recursion.is_validatable(),
363            Feature::Extensions(extensions) => extensions.is_validatable(),
364        }
365    }
366}
367
368macro_rules! is_validatable {
369    ( $( $ty:path $( = $validatable:literal )? ),* ) => {
370        $(
371            impl Validatable for $ty {
372            $(
373                fn is_validatable(&self) -> bool {
374                    $validatable
375                }
376            )?
377            }
378        )*
379    };
380}
381
382is_validatable! {
383    attributes::Default,
384    attributes::Example,
385    attributes::Examples,
386    attributes::XmlAttr,
387    attributes::Format,
388    attributes::WriteOnly,
389    attributes::ReadOnly,
390    attributes::Title,
391    attributes::Nullable,
392    attributes::Rename,
393    attributes::RenameAll,
394    attributes::Style,
395    attributes::ParameterIn,
396    attributes::AllowReserved,
397    attributes::Explode,
398    attributes::ValueType,
399    attributes::Inline,
400    attributes::IntoParamsNames,
401    attributes::SchemaWith,
402    attributes::Description,
403    attributes::Deprecated,
404    attributes::As,
405    attributes::AdditionalProperties,
406    attributes::Required,
407    attributes::ContentEncoding,
408    attributes::ContentMediaType,
409    attributes::Discriminator,
410    attributes::Bound,
411    attributes::Ignore,
412    attributes::NoRecursion,
413    validation::MultipleOf = true,
414    validation::Maximum = true,
415    validation::Minimum = true,
416    validation::ExclusiveMaximum = true,
417    validation::ExclusiveMinimum = true,
418    validation::MaxLength = true,
419    validation::MinLength = true,
420    validation::Pattern = true,
421    validation::MaxItems = true,
422    validation::MinItems = true,
423    validation::MaxProperties,
424    validation::MinProperties,
425    attributes::Extensions
426}
427
428macro_rules! parse_features {
429    ($ident:ident as $( $feature:path ),*) => {
430        {
431            fn parse(input: syn::parse::ParseStream) -> syn::Result<Vec<crate::component::features::Feature>> {
432                let names = [$( <crate::component::features::parse_features!(@as_ident $feature) as crate::component::features::FeatureLike>::get_name(), )* ];
433                let mut features = Vec::<crate::component::features::Feature>::new();
434                let attributes = names.join(", ");
435
436                while !input.is_empty() {
437                    let ident = input.parse::<syn::Ident>().or_else(|_| {
438                        input.parse::<syn::Token![as]>().map(|as_| syn::Ident::new("as", as_.span))
439                    }).map_err(|error| {
440                        syn::Error::new(
441                            error.span(),
442                            format!("unexpected attribute, expected any of: {attributes}, {error}"),
443                        )
444                    })?;
445                    let name = &*ident.to_string();
446
447                    $(
448                        if name == <crate::component::features::parse_features!(@as_ident $feature) as crate::component::features::FeatureLike>::get_name() {
449                            features.push(<$feature as crate::component::features::Parse>::parse(input, ident)?.into());
450                            if !input.is_empty() {
451                                input.parse::<syn::Token![,]>()?;
452                            }
453                            continue;
454                        }
455                    )*
456
457                    if !names.contains(&std::borrow::Cow::Borrowed(name)) {
458                        return Err(syn::Error::new(ident.span(), format!("unexpected attribute: {name}, expected any of: {attributes}")))
459                    }
460                }
461
462                Ok(features)
463            }
464
465            parse($ident)?
466        }
467    };
468    (@as_ident $( $tt:tt )* ) => {
469        $( $tt )*
470    }
471}
472
473pub(crate) use parse_features;
474
475pub trait IsInline {
476    fn is_inline(&self) -> bool;
477}
478
479impl IsInline for Vec<Feature> {
480    fn is_inline(&self) -> bool {
481        self.iter()
482            .find_map(|feature| match feature {
483                Feature::Inline(inline) if inline.0 => Some(inline),
484                _ => None,
485            })
486            .is_some()
487    }
488}
489
490pub trait ToTokensExt {
491    fn to_token_stream(&self) -> Result<TokenStream, Diagnostics>;
492}
493
494impl ToTokensExt for Vec<Feature> {
495    fn to_token_stream(&self) -> Result<TokenStream, Diagnostics> {
496        Ok(self
497            .iter()
498            .map(|feature| Ok(as_tokens_or_diagnostics!(feature)))
499            .collect::<Result<Vec<TokenStream>, Diagnostics>>()?
500            .into_iter()
501            .fold(TokenStream::new(), |mut tokens, item| {
502                item.to_tokens(&mut tokens);
503                tokens
504            }))
505    }
506}
507
508pub trait FeaturesExt {
509    fn pop_by(&mut self, op: impl FnMut(&Feature) -> bool) -> Option<Feature>;
510
511    /// Extract [`XmlAttr`] feature for given `type_tree` if it has generic type [`GenericType::Vec`]
512    fn extract_vec_xml_feature(
513        &mut self,
514        type_tree: &TypeTree,
515    ) -> Result<Option<Feature>, Diagnostics>;
516}
517
518impl FeaturesExt for Vec<Feature> {
519    fn pop_by(&mut self, op: impl FnMut(&Feature) -> bool) -> Option<Feature> {
520        self.iter()
521            .position(op)
522            .map(|index| self.swap_remove(index))
523    }
524
525    fn extract_vec_xml_feature(
526        &mut self,
527        type_tree: &TypeTree,
528    ) -> Result<Option<Feature>, Diagnostics> {
529        self.iter_mut()
530            .find_map(|feature| match feature {
531                Feature::XmlAttr(xml_feature) => {
532                    match xml_feature.split_for_vec(type_tree) {
533                        Ok((vec_xml, value_xml)) => {
534                            // replace the original xml attribute with split value xml
535                            if let Some(mut xml) = value_xml {
536                                mem::swap(xml_feature, &mut xml)
537                            }
538
539                            Some(Ok(vec_xml.map(Feature::XmlAttr)))
540                        }
541                        Err(diagnostics) => Some(Err(diagnostics)),
542                    }
543                }
544                _ => None,
545            })
546            .and_then_try(|value| value)
547    }
548}
549
550impl FeaturesExt for Option<Vec<Feature>> {
551    fn pop_by(&mut self, op: impl FnMut(&Feature) -> bool) -> Option<Feature> {
552        self.as_mut().and_then(|features| features.pop_by(op))
553    }
554
555    fn extract_vec_xml_feature(
556        &mut self,
557        type_tree: &TypeTree,
558    ) -> Result<Option<Feature>, Diagnostics> {
559        self.as_mut()
560            .and_then_try(|features| features.extract_vec_xml_feature(type_tree))
561    }
562}
563
564/// Pull out a `Feature` from `Vec` of features by given match predicate.
565/// This macro can be called in two forms demonstrated below.
566/// ```text
567///  let _: Option<Feature> = pop_feature!(features => Feature::Inline(_));
568///  let _: Option<Inline> = pop_feature!(feature => Feature::Inline(_) as Option<Inline>);
569/// ```
570///
571/// The `as ...` syntax can be used to directly convert the `Feature` instance to it's inner form.
572macro_rules! pop_feature {
573    ($features:ident => $( $ty:tt )* ) => {{
574        pop_feature!( @inner $features $( $ty )* )
575    }};
576    ( @inner $features:ident $ty:tt :: $tv:tt ( $t:pat ) $( $tt:tt)* ) => {
577        {
578        let f = $features.pop_by(|feature| matches!(feature, $ty :: $tv ($t) ) );
579        pop_feature!( @rest f $( $tt )* )
580        }
581    };
582    ( @rest $feature:ident as $ty:ty ) => {
583        {
584        let inner: $ty = $feature.into_inner();
585        inner
586        }
587
588    };
589    ( @rest $($tt:tt)* ) => {
590        $($tt)*
591    };
592}
593
594pub(crate) use pop_feature;
595
596pub trait IntoInner<T> {
597    fn into_inner(self) -> T;
598}
599
600macro_rules! impl_feature_into_inner {
601    ( $( $feat:ident :: $impl:ident , )* ) => {
602        $(
603            impl IntoInner<Option<$feat::$impl>> for Option<Feature> {
604                fn into_inner(self) -> Option<$feat::$impl> {
605                    self.and_then(|feature| match feature {
606                        Feature::$impl(value) => Some(value),
607                        _ => None,
608                    })
609                }
610            }
611        )*
612    };
613}
614
615impl_feature_into_inner! {
616    attributes::Example,
617    attributes::Examples,
618    attributes::Default,
619    attributes::Inline,
620    attributes::XmlAttr,
621    attributes::Format,
622    attributes::ValueType,
623    attributes::WriteOnly,
624    attributes::ReadOnly,
625    attributes::Title,
626    attributes::Nullable,
627    attributes::Rename,
628    attributes::RenameAll,
629    attributes::Style,
630    attributes::AllowReserved,
631    attributes::Explode,
632    attributes::ParameterIn,
633    attributes::IntoParamsNames,
634    attributes::SchemaWith,
635    attributes::Description,
636    attributes::Deprecated,
637    attributes::As,
638    attributes::Required,
639    attributes::AdditionalProperties,
640    attributes::Discriminator,
641    attributes::Bound,
642    attributes::Ignore,
643    attributes::NoRecursion,
644    validation::MultipleOf,
645    validation::Maximum,
646    validation::Minimum,
647    validation::ExclusiveMaximum,
648    validation::ExclusiveMinimum,
649    validation::MaxLength,
650    validation::MinLength,
651    validation::Pattern,
652    validation::MaxItems,
653    validation::MinItems,
654    validation::MaxProperties,
655    validation::MinProperties,
656}
657
658macro_rules! impl_into_inner {
659    ($ident:ident) => {
660        impl crate::component::features::IntoInner<Vec<Feature>> for $ident {
661            fn into_inner(self) -> Vec<Feature> {
662                self.0
663            }
664        }
665
666        impl crate::component::features::IntoInner<Option<Vec<Feature>>> for Option<$ident> {
667            fn into_inner(self) -> Option<Vec<Feature>> {
668                self.map(crate::component::features::IntoInner::into_inner)
669            }
670        }
671    };
672}
673
674pub(crate) use impl_into_inner;
675
676pub trait Merge<T>: IntoInner<Vec<Feature>> {
677    fn merge(self, from: T) -> Self;
678}
679
680macro_rules! impl_merge {
681    ( $($ident:ident),* ) => {
682        $(
683            impl AsMut<Vec<Feature>> for $ident {
684                fn as_mut(&mut self) -> &mut Vec<Feature> {
685                    &mut self.0
686                }
687            }
688
689            impl crate::component::features::Merge<$ident> for $ident {
690                fn merge(mut self, from: $ident) -> Self {
691                    use $crate::component::features::IntoInner;
692                    let a = self.as_mut();
693                    let mut b = from.into_inner();
694
695                    a.append(&mut b);
696
697                    self
698                }
699            }
700        )*
701    };
702}
703
704pub(crate) use impl_merge;
705
706impl IntoInner<Vec<Feature>> for Vec<Feature> {
707    fn into_inner(self) -> Vec<Feature> {
708        self
709    }
710}
711
712impl Merge<Vec<Feature>> for Vec<Feature> {
713    fn merge(mut self, mut from: Vec<Feature>) -> Self {
714        self.append(&mut from);
715        self
716    }
717}