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
56pub trait Validatable {
58 fn is_validatable(&self) -> bool {
59 false
60 }
61}
62
63pub trait Validate: Validatable {
64 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 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 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 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 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
564macro_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}