1use std::borrow::Cow;
2
3use proc_macro2::{Ident, Span, TokenStream};
4use proc_macro_error::abort;
5use quote::{format_ident, quote, ToTokens};
6use syn::{
7 parse::Parse, parse_quote, punctuated::Punctuated, spanned::Spanned, token::Comma, Attribute,
8 Data, Field, Fields, FieldsNamed, FieldsUnnamed, GenericArgument, GenericParam, Generics,
9 Lifetime, LifetimeParam, Path, PathArguments, Token, Type, Variant, Visibility,
10};
11
12use crate::{
13 component::features::{Example, Rename},
14 doc_comment::CommentAttributes,
15 Array, Deprecated, ResultExt,
16};
17
18use self::{
19 enum_variant::{
20 AdjacentlyTaggedEnum, CustomEnum, Enum, ObjectVariant, SimpleEnumVariant, TaggedEnum,
21 UntaggedEnum,
22 },
23 features::{
24 ComplexEnumFeatures, EnumFeatures, EnumNamedFieldVariantFeatures,
25 EnumUnnamedFieldVariantFeatures, FromAttributes, NamedFieldFeatures,
26 NamedFieldStructFeatures, UnnamedFieldStructFeatures,
27 },
28};
29
30use super::{
31 features::{
32 parse_features, pop_feature, pop_feature_as_inner, As, Feature, FeaturesExt, IntoInner,
33 RenameAll, ToTokensExt,
34 },
35 serde::{self, SerdeContainer, SerdeEnumRepr, SerdeValue},
36 ComponentSchema, FieldRename, TypeTree, ValueType, VariantRename,
37};
38
39mod enum_variant;
40mod features;
41pub mod xml;
42
43pub struct Schema<'a> {
44 ident: &'a Ident,
45 attributes: &'a [Attribute],
46 generics: &'a Generics,
47 aliases: Option<Punctuated<AliasSchema, Comma>>,
48 data: &'a Data,
49 vis: &'a Visibility,
50}
51
52impl<'a> Schema<'a> {
53 const TO_SCHEMA_LIFETIME: &'static str = "'__s";
54 pub fn new(
55 data: &'a Data,
56 attributes: &'a [Attribute],
57 ident: &'a Ident,
58 generics: &'a Generics,
59 vis: &'a Visibility,
60 ) -> Self {
61 let aliases = if generics.type_params().count() > 0 {
62 parse_aliases(attributes)
63 } else {
64 None
65 };
66
67 Self {
68 data,
69 ident,
70 attributes,
71 generics,
72 aliases,
73 vis,
74 }
75 }
76}
77
78impl ToTokens for Schema<'_> {
79 fn to_tokens(&self, tokens: &mut TokenStream) {
80 let ident = self.ident;
81 let variant = SchemaVariant::new(
82 self.data,
83 self.attributes,
84 ident,
85 self.generics,
86 None::<Vec<(TypeTree, &TypeTree)>>,
87 );
88
89 let (_, ty_generics, where_clause) = self.generics.split_for_impl();
90
91 let life = &Lifetime::new(Schema::TO_SCHEMA_LIFETIME, Span::call_site());
92
93 let schema_ty: Type = parse_quote!(#ident #ty_generics);
94 let schema_children = &*TypeTree::from_type(&schema_ty).children.unwrap_or_default();
95
96 let aliases = self.aliases.as_ref().map(|aliases| {
97 let alias_schemas = aliases
98 .iter()
99 .map(|alias| {
100 let name = &*alias.name;
101 let alias_type_tree = TypeTree::from_type(&alias.ty);
102
103 let variant = SchemaVariant::new(
104 self.data,
105 self.attributes,
106 ident,
107 self.generics,
108 alias_type_tree
109 .children
110 .map(|children| children.into_iter().zip(schema_children)),
111 );
112 quote! { (#name, #variant.into()) }
113 })
114 .collect::<Array<TokenStream>>();
115
116 quote! {
117 fn aliases() -> Vec<(& #life str, utoipa::openapi::schema::Schema)> {
118 #alias_schemas.to_vec()
119 }
120 }
121 });
122
123 let type_aliases = self.aliases.as_ref().map(|aliases| {
124 aliases
125 .iter()
126 .map(|alias| {
127 let name = quote::format_ident!("{}", alias.name);
128 let ty = &alias.ty;
129 let vis = self.vis;
130 let name_generics = alias.get_lifetimes().fold(
131 Punctuated::<&GenericArgument, Comma>::new(),
132 |mut acc, lifetime| {
133 acc.push(lifetime);
134 acc
135 },
136 );
137
138 quote! {
139 #vis type #name < #name_generics > = #ty;
140 }
141 })
142 .collect::<TokenStream>()
143 });
144
145 let name = if let Some(schema_as) = variant.get_schema_as() {
146 format_path_ref(&schema_as.0.path)
147 } else {
148 ident.to_string()
149 };
150
151 let schema_lifetime: GenericParam = LifetimeParam::new(life.clone()).into();
152 let schema_generics = Generics {
153 params: [schema_lifetime.clone()].into_iter().collect(),
154 ..Default::default()
155 };
156
157 let mut impl_generics = self.generics.clone();
158 impl_generics.params.push(schema_lifetime);
159 let (impl_generics, _, _) = impl_generics.split_for_impl();
160
161 tokens.extend(quote! {
162 impl #impl_generics utoipa::ToSchema #schema_generics for #ident #ty_generics #where_clause {
163 fn schema() -> (& #life str, utoipa::openapi::RefOr<utoipa::openapi::schema::Schema>) {
164 (#name, #variant.into())
165 }
166
167 #aliases
168 }
169
170 #type_aliases
171 })
172 }
173}
174
175#[cfg_attr(feature = "debug", derive(Debug))]
176enum SchemaVariant<'a> {
177 Named(NamedStructSchema<'a>),
178 Unnamed(UnnamedStructSchema<'a>),
179 Enum(EnumSchema<'a>),
180 Unit(UnitStructVariant),
181}
182
183impl<'a> SchemaVariant<'a> {
184 pub fn new<I: IntoIterator<Item = (TypeTree<'a>, &'a TypeTree<'a>)>>(
185 data: &'a Data,
186 attributes: &'a [Attribute],
187 ident: &'a Ident,
188 generics: &'a Generics,
189 aliases: Option<I>,
190 ) -> SchemaVariant<'a> {
191 match data {
192 Data::Struct(content) => match &content.fields {
193 Fields::Unnamed(fields) => {
194 let FieldsUnnamed { unnamed, .. } = fields;
195 let mut unnamed_features = attributes
196 .parse_features::<UnnamedFieldStructFeatures>()
197 .into_inner();
198
199 let schema_as = pop_feature_as_inner!(unnamed_features => Feature::As(_v));
200 Self::Unnamed(UnnamedStructSchema {
201 struct_name: Cow::Owned(ident.to_string()),
202 attributes,
203 features: unnamed_features,
204 fields: unnamed,
205 schema_as,
206 })
207 }
208 Fields::Named(fields) => {
209 let FieldsNamed { named, .. } = fields;
210 let mut named_features = attributes
211 .parse_features::<NamedFieldStructFeatures>()
212 .into_inner();
213 let schema_as = pop_feature_as_inner!(named_features => Feature::As(_v));
214
215 Self::Named(NamedStructSchema {
216 struct_name: Cow::Owned(ident.to_string()),
217 attributes,
218 rename_all: named_features.pop_rename_all_feature(),
219 features: named_features,
220 fields: named,
221 generics: Some(generics),
222 schema_as,
223 aliases: aliases.map(|aliases| aliases.into_iter().collect()),
224 })
225 }
226 Fields::Unit => Self::Unit(UnitStructVariant),
227 },
228 Data::Enum(content) => Self::Enum(EnumSchema::new(
229 Cow::Owned(ident.to_string()),
230 &content.variants,
231 attributes,
232 )),
233 _ => abort!(
234 ident.span(),
235 "unexpected data type, expected syn::Data::Struct or syn::Data::Enum"
236 ),
237 }
238 }
239
240 fn get_schema_as(&self) -> &Option<As> {
241 match self {
242 Self::Enum(schema) => &schema.schema_as,
243 Self::Named(schema) => &schema.schema_as,
244 Self::Unnamed(schema) => &schema.schema_as,
245 _ => &None,
246 }
247 }
248}
249
250impl ToTokens for SchemaVariant<'_> {
251 fn to_tokens(&self, tokens: &mut TokenStream) {
252 match self {
253 Self::Enum(schema) => schema.to_tokens(tokens),
254 Self::Named(schema) => schema.to_tokens(tokens),
255 Self::Unnamed(schema) => schema.to_tokens(tokens),
256 Self::Unit(unit) => unit.to_tokens(tokens),
257 }
258 }
259}
260
261#[cfg_attr(feature = "debug", derive(Debug))]
262struct UnitStructVariant;
263
264impl ToTokens for UnitStructVariant {
265 fn to_tokens(&self, tokens: &mut TokenStream) {
266 tokens.extend(quote! {
267 utoipa::openapi::schema::empty()
268 });
269 }
270}
271
272#[cfg_attr(feature = "debug", derive(Debug))]
273pub struct NamedStructSchema<'a> {
274 pub struct_name: Cow<'a, str>,
275 pub fields: &'a Punctuated<Field, Comma>,
276 pub attributes: &'a [Attribute],
277 pub features: Option<Vec<Feature>>,
278 pub rename_all: Option<RenameAll>,
279 pub generics: Option<&'a Generics>,
280 pub aliases: Option<Vec<(TypeTree<'a>, &'a TypeTree<'a>)>>,
281 pub schema_as: Option<As>,
282}
283
284struct NamedStructFieldOptions<'a> {
285 property: Property,
286 rename_field_value: Option<Cow<'a, str>>,
287 required: Option<super::features::Required>,
288 is_option: bool,
289}
290
291impl NamedStructSchema<'_> {
292 fn field_as_schema_property<R>(
293 &self,
294 field: &Field,
295 container_rules: &Option<SerdeContainer>,
296 yield_: impl FnOnce(NamedStructFieldOptions<'_>) -> R,
297 ) -> R {
298 let type_tree = &mut TypeTree::from_type(&field.ty);
299 if let Some(aliases) = &self.aliases {
300 for (new_generic, old_generic_matcher) in aliases.iter() {
301 if let Some(generic_match) = type_tree.find_mut(old_generic_matcher) {
302 *generic_match = new_generic.clone();
303 }
304 }
305 }
306
307 let mut field_features = field
308 .attrs
309 .parse_features::<NamedFieldFeatures>()
310 .into_inner();
311
312 let schema_default = self
313 .features
314 .as_ref()
315 .map(|features| features.iter().any(|f| matches!(f, Feature::Default(_))))
316 .unwrap_or(false);
317 let serde_default = container_rules
318 .as_ref()
319 .map(|rules| rules.default)
320 .unwrap_or(false);
321
322 if schema_default || serde_default {
323 let features_inner = field_features.get_or_insert(vec![]);
324 if !features_inner
325 .iter()
326 .any(|f| matches!(f, Feature::Default(_)))
327 {
328 let field_ident = field.ident.as_ref().unwrap().to_owned();
329 let struct_ident = format_ident!("{}", &self.struct_name);
330 features_inner.push(Feature::Default(
331 crate::features::Default::new_default_trait(struct_ident, field_ident.into()),
332 ));
333 }
334 }
335
336 let deprecated = super::get_deprecated(&field.attrs).or_else(|| {
338 pop_feature!(field_features => Feature::Deprecated(_)).and_then(|feature| match feature
339 {
340 Feature::Deprecated(_) => Some(Deprecated::True),
341 _ => None,
342 })
343 });
344
345 let rename_field =
346 pop_feature!(field_features => Feature::Rename(_)).and_then(|feature| match feature {
347 Feature::Rename(rename) => Some(Cow::Owned(rename.into_value())),
348 _ => None,
349 });
350
351 let value_type = field_features
352 .as_mut()
353 .and_then(|features| features.pop_value_type_feature());
354 let override_type_tree = value_type
355 .as_ref()
356 .map(|value_type| value_type.as_type_tree());
357 let comments = CommentAttributes::from_attributes(&field.attrs);
358 let schema_with = pop_feature!(field_features => Feature::SchemaWith(_));
359 let required = pop_feature_as_inner!(field_features => Feature::Required(_v));
360 let type_tree = override_type_tree.as_ref().unwrap_or(type_tree);
361 let is_option = type_tree.is_option();
362
363 yield_(NamedStructFieldOptions {
364 property: if let Some(schema_with) = schema_with {
365 Property::SchemaWith(schema_with)
366 } else {
367 Property::Schema(ComponentSchema::new(super::ComponentSchemaProps {
368 type_tree,
369 features: field_features,
370 description: Some(&comments),
371 deprecated: deprecated.as_ref(),
372 object_name: self.struct_name.as_ref(),
373 }))
374 },
375 rename_field_value: rename_field,
376 required,
377 is_option,
378 })
379 }
380}
381
382impl ToTokens for NamedStructSchema<'_> {
383 fn to_tokens(&self, tokens: &mut TokenStream) {
384 let container_rules = serde::parse_container(self.attributes);
385
386 let object_tokens = self
387 .fields
388 .iter()
389 .filter_map(|field| {
390 let field_rule = serde::parse_value(&field.attrs);
391
392 if is_not_skipped(&field_rule) && !is_flatten(&field_rule) {
393 Some((field, field_rule))
394 } else {
395 None
396 }
397 })
398 .fold(
399 quote! { utoipa::openapi::ObjectBuilder::new() },
400 |mut object_tokens, (field, field_rule)| {
401 let mut field_name = &*field.ident.as_ref().unwrap().to_string();
402
403 if field_name.starts_with("r#") {
404 field_name = &field_name[2..];
405 }
406
407 self.field_as_schema_property(
408 field,
409 &container_rules,
410 |NamedStructFieldOptions {
411 property,
412 rename_field_value,
413 required,
414 is_option,
415 }| {
416 let rename_to = field_rule
417 .as_ref()
418 .and_then(|field_rule| {
419 field_rule.rename.as_deref().map(Cow::Borrowed)
420 })
421 .or(rename_field_value);
422 let rename_all = container_rules
423 .as_ref()
424 .and_then(|container_rule| container_rule.rename_all.as_ref())
425 .or_else(|| {
426 self.rename_all
427 .as_ref()
428 .map(|rename_all| rename_all.as_rename_rule())
429 });
430
431 let name =
432 super::rename::<FieldRename>(field_name, rename_to, rename_all)
433 .unwrap_or(Cow::Borrowed(field_name));
434
435 object_tokens.extend(quote! {
436 .property(#name, #property)
437 });
438
439 if let Property::Schema(_) = property {
440 if (!is_option
441 && super::is_required(
442 field_rule.as_ref(),
443 container_rules.as_ref(),
444 ))
445 || required
446 .as_ref()
447 .map(super::features::Required::is_true)
448 .unwrap_or(false)
449 {
450 object_tokens.extend(quote! {
451 .required(#name)
452 })
453 }
454 }
455
456 object_tokens
457 },
458 )
459 },
460 );
461
462 let flatten_fields: Vec<&Field> = self
463 .fields
464 .iter()
465 .filter(|field| {
466 let field_rule = serde::parse_value(&field.attrs);
467 is_flatten(&field_rule)
468 })
469 .collect();
470
471 if !flatten_fields.is_empty() {
472 tokens.extend(quote! {
473 utoipa::openapi::AllOfBuilder::new()
474 });
475
476 for field in flatten_fields {
477 self.field_as_schema_property(
478 field,
479 &container_rules,
480 |NamedStructFieldOptions { property, .. }| {
481 tokens.extend(quote! { .item(#property) });
482 },
483 )
484 }
485
486 tokens.extend(quote! {
487 .item(#object_tokens)
488 })
489 } else {
490 tokens.extend(object_tokens)
491 }
492
493 if let Some(deprecated) = super::get_deprecated(self.attributes) {
494 tokens.extend(quote! { .deprecated(Some(#deprecated)) });
495 }
496
497 if let Some(struct_features) = self.features.as_ref() {
498 tokens.extend(struct_features.to_token_stream())
499 }
500
501 let description = CommentAttributes::from_attributes(self.attributes).as_formatted_string();
502 if !description.is_empty() {
503 tokens.extend(quote! {
504 .description(Some(#description))
505 })
506 }
507 }
508}
509
510#[cfg_attr(feature = "debug", derive(Debug))]
511struct UnnamedStructSchema<'a> {
512 struct_name: Cow<'a, str>,
513 fields: &'a Punctuated<Field, Comma>,
514 attributes: &'a [Attribute],
515 features: Option<Vec<Feature>>,
516 schema_as: Option<As>,
517}
518
519impl ToTokens for UnnamedStructSchema<'_> {
520 fn to_tokens(&self, tokens: &mut TokenStream) {
521 let fields_len = self.fields.len();
522 let first_field = self.fields.first().unwrap();
523 let first_part = &TypeTree::from_type(&first_field.ty);
524
525 let all_fields_are_same = fields_len == 1
526 || self.fields.iter().skip(1).all(|field| {
527 let schema_part = &TypeTree::from_type(&field.ty);
528
529 first_part == schema_part
530 });
531
532 let deprecated = super::get_deprecated(self.attributes);
533 if all_fields_are_same {
534 let mut unnamed_struct_features = self.features.clone();
535 let value_type = unnamed_struct_features
536 .as_mut()
537 .and_then(|features| features.pop_value_type_feature());
538 let override_type_tree = value_type
539 .as_ref()
540 .map(|value_type| value_type.as_type_tree());
541
542 if fields_len == 1 {
543 if let Some(ref mut features) = unnamed_struct_features {
544 if pop_feature!(features => Feature::Default(crate::features::Default(None)))
545 .is_some()
546 {
547 let struct_ident = format_ident!("{}", &self.struct_name);
548 let index: syn::Index = 0.into();
549 features.push(Feature::Default(
550 crate::features::Default::new_default_trait(struct_ident, index.into()),
551 ));
552 }
553 }
554 }
555
556 tokens.extend(
557 ComponentSchema::new(super::ComponentSchemaProps {
558 type_tree: override_type_tree.as_ref().unwrap_or(first_part),
559 features: unnamed_struct_features,
560 description: Some(&CommentAttributes::from_attributes(self.attributes)),
561 deprecated: deprecated.as_ref(),
562 object_name: self.struct_name.as_ref(),
563 })
564 .to_token_stream(),
565 );
566 } else {
567 tokens.extend(quote! {
572 utoipa::openapi::ObjectBuilder::new()
573 });
574
575 if let Some(deprecated) = deprecated {
576 tokens.extend(quote! { .deprecated(Some(#deprecated)) });
577 }
578
579 if let Some(ref attrs) = self.features {
580 tokens.extend(attrs.to_token_stream())
581 }
582 }
583
584 if fields_len > 1 {
585 let description =
586 CommentAttributes::from_attributes(self.attributes).as_formatted_string();
587 tokens.extend(
588 quote! { .to_array_builder().description(Some(#description)).max_items(Some(#fields_len)).min_items(Some(#fields_len)) },
589 )
590 }
591 }
592}
593
594#[cfg_attr(feature = "debug", derive(Debug))]
595pub struct EnumSchema<'a> {
596 schema_type: EnumSchemaType<'a>,
597 schema_as: Option<As>,
598}
599
600impl<'e> EnumSchema<'e> {
601 pub fn new(
602 enum_name: Cow<'e, str>,
603 variants: &'e Punctuated<Variant, Comma>,
604 attributes: &'e [Attribute],
605 ) -> Self {
606 if variants
607 .iter()
608 .all(|variant| matches!(variant.fields, Fields::Unit))
609 {
610 #[cfg(feature = "repr")]
611 {
612 attributes
613 .iter()
614 .find_map(|attribute| {
615 if attribute.path().is_ident("repr") {
616 attribute.parse_args::<syn::TypePath>().ok()
617 } else {
618 None
619 }
620 })
621 .map(|enum_type| {
622 let mut repr_enum_features =
623 features::parse_schema_features_with(attributes, |input| {
624 Ok(parse_features!(
625 input as super::features::Example,
626 super::features::Default,
627 super::features::Title,
628 As
629 ))
630 })
631 .unwrap_or_default();
632
633 let schema_as =
634 pop_feature_as_inner!(repr_enum_features => Feature::As(_v));
635 Self {
636 schema_type: EnumSchemaType::Repr(ReprEnum {
637 variants,
638 attributes,
639 enum_type,
640 enum_features: repr_enum_features,
641 }),
642 schema_as,
643 }
644 })
645 .unwrap_or_else(|| {
646 let mut simple_enum_features = attributes
647 .parse_features::<EnumFeatures>()
648 .into_inner()
649 .unwrap_or_default();
650 let schema_as =
651 pop_feature_as_inner!(simple_enum_features => Feature::As(_v));
652 let rename_all = simple_enum_features.pop_rename_all_feature();
653
654 Self {
655 schema_type: EnumSchemaType::Simple(SimpleEnum {
656 attributes,
657 variants,
658 enum_features: simple_enum_features,
659 rename_all,
660 }),
661 schema_as,
662 }
663 })
664 }
665
666 #[cfg(not(feature = "repr"))]
667 {
668 let mut simple_enum_features = attributes
669 .parse_features::<EnumFeatures>()
670 .into_inner()
671 .unwrap_or_default();
672 let schema_as = pop_feature_as_inner!(simple_enum_features => Feature::As(_v));
673 let rename_all = simple_enum_features.pop_rename_all_feature();
674
675 Self {
676 schema_type: EnumSchemaType::Simple(SimpleEnum {
677 attributes,
678 variants,
679 enum_features: simple_enum_features,
680 rename_all,
681 }),
682 schema_as,
683 }
684 }
685 } else {
686 let mut enum_features = attributes
687 .parse_features::<ComplexEnumFeatures>()
688 .into_inner()
689 .unwrap_or_default();
690 let schema_as = pop_feature_as_inner!(enum_features => Feature::As(_v));
691 let rename_all = enum_features.pop_rename_all_feature();
692
693 Self {
694 schema_type: EnumSchemaType::Complex(ComplexEnum {
695 enum_name,
696 attributes,
697 variants,
698 rename_all,
699 enum_features,
700 }),
701 schema_as,
702 }
703 }
704 }
705}
706
707impl ToTokens for EnumSchema<'_> {
708 fn to_tokens(&self, tokens: &mut TokenStream) {
709 self.schema_type.to_tokens(tokens);
710 }
711}
712
713#[cfg_attr(feature = "debug", derive(Debug))]
714enum EnumSchemaType<'e> {
715 Simple(SimpleEnum<'e>),
716 #[cfg(feature = "repr")]
717 Repr(ReprEnum<'e>),
718 Complex(ComplexEnum<'e>),
719}
720
721impl ToTokens for EnumSchemaType<'_> {
722 fn to_tokens(&self, tokens: &mut TokenStream) {
723 let attributes = match self {
724 Self::Simple(simple) => {
725 simple.to_tokens(tokens);
726 simple.attributes
727 }
728 #[cfg(feature = "repr")]
729 Self::Repr(repr) => {
730 repr.to_tokens(tokens);
731 repr.attributes
732 }
733 Self::Complex(complex) => {
734 complex.to_tokens(tokens);
735 complex.attributes
736 }
737 };
738
739 if let Some(deprecated) = super::get_deprecated(attributes) {
740 tokens.extend(quote! { .deprecated(Some(#deprecated)) });
741 }
742
743 let description = CommentAttributes::from_attributes(attributes).as_formatted_string();
744 if !description.is_empty() {
745 tokens.extend(quote! {
746 .description(Some(#description))
747 })
748 }
749 }
750}
751
752#[cfg(feature = "repr")]
753#[cfg_attr(feature = "debug", derive(Debug))]
754struct ReprEnum<'a> {
755 variants: &'a Punctuated<Variant, Comma>,
756 attributes: &'a [Attribute],
757 enum_type: syn::TypePath,
758 enum_features: Vec<Feature>,
759}
760
761#[cfg(feature = "repr")]
762impl ToTokens for ReprEnum<'_> {
763 fn to_tokens(&self, tokens: &mut TokenStream) {
764 let container_rules = serde::parse_container(self.attributes);
765
766 regular_enum_to_tokens(tokens, &container_rules, &self.enum_features, || {
767 self.variants
768 .iter()
769 .filter_map(|variant| {
770 let variant_type = &variant.ident;
771 let variant_rules = serde::parse_value(&variant.attrs);
772
773 if is_not_skipped(&variant_rules) {
774 let repr_type = &self.enum_type;
775 Some(enum_variant::ReprVariant {
776 value: quote! { Self::#variant_type as #repr_type },
777 type_path: repr_type,
778 })
779 } else {
780 None
781 }
782 })
783 .collect::<Vec<enum_variant::ReprVariant<TokenStream>>>()
784 });
785 }
786}
787
788fn rename_enum_variant<'a>(
789 name: &'a str,
790 features: &mut Vec<Feature>,
791 variant_rules: &'a Option<SerdeValue>,
792 container_rules: &'a Option<SerdeContainer>,
793 rename_all: &'a Option<RenameAll>,
794) -> Option<Cow<'a, str>> {
795 let rename = features
796 .pop_rename_feature()
797 .map(|rename| rename.into_value());
798 let rename_to = variant_rules
799 .as_ref()
800 .and_then(|variant_rules| variant_rules.rename.as_deref().map(Cow::Borrowed))
801 .or_else(|| rename.map(Cow::Owned));
802
803 let rename_all = container_rules
804 .as_ref()
805 .and_then(|container_rules| container_rules.rename_all.as_ref())
806 .or_else(|| {
807 rename_all
808 .as_ref()
809 .map(|rename_all| rename_all.as_rename_rule())
810 });
811
812 super::rename::<VariantRename>(name, rename_to, rename_all)
813}
814
815#[cfg_attr(feature = "debug", derive(Debug))]
816struct SimpleEnum<'a> {
817 variants: &'a Punctuated<Variant, Comma>,
818 attributes: &'a [Attribute],
819 enum_features: Vec<Feature>,
820 rename_all: Option<RenameAll>,
821}
822
823impl ToTokens for SimpleEnum<'_> {
824 fn to_tokens(&self, tokens: &mut TokenStream) {
825 let container_rules = serde::parse_container(self.attributes);
826
827 regular_enum_to_tokens(tokens, &container_rules, &self.enum_features, || {
828 self.variants
829 .iter()
830 .filter_map(|variant| {
831 let variant_rules = serde::parse_value(&variant.attrs);
832
833 if is_not_skipped(&variant_rules) {
834 Some((variant, variant_rules))
835 } else {
836 None
837 }
838 })
839 .flat_map(|(variant, variant_rules)| {
840 let name = &*variant.ident.to_string();
841 let mut variant_features =
842 features::parse_schema_features_with(&variant.attrs, |input| {
843 Ok(parse_features!(input as Rename))
844 })
845 .unwrap_or_default();
846 let variant_name = rename_enum_variant(
847 name,
848 &mut variant_features,
849 &variant_rules,
850 &container_rules,
851 &self.rename_all,
852 );
853
854 variant_name
855 .map(|name| SimpleEnumVariant {
856 value: name.to_token_stream(),
857 })
858 .or_else(|| {
859 Some(SimpleEnumVariant {
860 value: name.to_token_stream(),
861 })
862 })
863 })
864 .collect::<Vec<SimpleEnumVariant<TokenStream>>>()
865 });
866 }
867}
868
869fn regular_enum_to_tokens<T: self::enum_variant::Variant>(
870 tokens: &mut TokenStream,
871 container_rules: &Option<SerdeContainer>,
872 enum_variant_features: &Vec<Feature>,
873 get_variants_tokens_vec: impl FnOnce() -> Vec<T>,
874) {
875 let enum_values = get_variants_tokens_vec();
876
877 tokens.extend(match container_rules {
878 Some(serde_container) => match &serde_container.enum_repr {
879 SerdeEnumRepr::ExternallyTagged => Enum::new(enum_values).to_token_stream(),
880 SerdeEnumRepr::InternallyTagged { tag } => TaggedEnum::new(
881 enum_values
882 .into_iter()
883 .map(|variant| (Cow::Borrowed(tag.as_str()), variant)),
884 )
885 .to_token_stream(),
886 SerdeEnumRepr::Untagged => UntaggedEnum::new().to_token_stream(),
887 SerdeEnumRepr::AdjacentlyTagged { tag, content } => {
888 AdjacentlyTaggedEnum::new(enum_values.into_iter().map(|variant| {
889 (
890 Cow::Borrowed(tag.as_str()),
891 Cow::Borrowed(content.as_str()),
892 variant,
893 )
894 }))
895 .to_token_stream()
896 }
897 SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => panic!("Invalid serde enum repr"),
899 },
900 _ => Enum::new(enum_values).to_token_stream(),
901 });
902
903 tokens.extend(enum_variant_features.to_token_stream());
904}
905
906#[cfg_attr(feature = "debug", derive(Debug))]
907struct ComplexEnum<'a> {
908 variants: &'a Punctuated<Variant, Comma>,
909 attributes: &'a [Attribute],
910 enum_name: Cow<'a, str>,
911 enum_features: Vec<Feature>,
912 rename_all: Option<RenameAll>,
913}
914
915impl ComplexEnum<'_> {
916 fn variant_tokens(
918 &self,
919 name: Cow<'_, str>,
920 variant: &Variant,
921 variant_rules: &Option<SerdeValue>,
922 container_rules: &Option<SerdeContainer>,
923 rename_all: &Option<RenameAll>,
924 ) -> TokenStream {
925 match &variant.fields {
927 Fields::Named(named_fields) => {
928 let (title_features, mut named_struct_features) = variant
929 .attrs
930 .parse_features::<EnumNamedFieldVariantFeatures>()
931 .into_inner()
932 .map(|features| features.split_for_title())
933 .unwrap_or_default();
934 let variant_name = rename_enum_variant(
935 name.as_ref(),
936 &mut named_struct_features,
937 variant_rules,
938 container_rules,
939 rename_all,
940 );
941
942 let example = pop_feature!(named_struct_features => Feature::Example(_));
943
944 self::enum_variant::Variant::to_tokens(&ObjectVariant {
945 name: variant_name.unwrap_or(Cow::Borrowed(&name)),
946 title: title_features.first().map(ToTokens::to_token_stream),
947 example: example.as_ref().map(ToTokens::to_token_stream),
948 item: NamedStructSchema {
949 struct_name: Cow::Borrowed(&*self.enum_name),
950 attributes: &variant.attrs,
951 rename_all: named_struct_features.pop_rename_all_feature(),
952 features: Some(named_struct_features),
953 fields: &named_fields.named,
954 generics: None,
955 aliases: None,
956 schema_as: None,
957 },
958 })
959 }
960 Fields::Unnamed(unnamed_fields) => {
961 let (title_features, mut unnamed_struct_features) = variant
962 .attrs
963 .parse_features::<EnumUnnamedFieldVariantFeatures>()
964 .into_inner()
965 .map(|features| features.split_for_title())
966 .unwrap_or_default();
967 let variant_name = rename_enum_variant(
968 name.as_ref(),
969 &mut unnamed_struct_features,
970 variant_rules,
971 container_rules,
972 rename_all,
973 );
974
975 let example = pop_feature!(unnamed_struct_features => Feature::Example(_));
976
977 self::enum_variant::Variant::to_tokens(&ObjectVariant {
978 name: variant_name.unwrap_or(Cow::Borrowed(&name)),
979 title: title_features.first().map(ToTokens::to_token_stream),
980 example: example.as_ref().map(ToTokens::to_token_stream),
981 item: UnnamedStructSchema {
982 struct_name: Cow::Borrowed(&*self.enum_name),
983 attributes: &variant.attrs,
984 features: Some(unnamed_struct_features),
985 fields: &unnamed_fields.unnamed,
986 schema_as: None,
987 },
988 })
989 }
990 Fields::Unit => {
991 let mut unit_features =
992 features::parse_schema_features_with(&variant.attrs, |input| {
993 Ok(parse_features!(
994 input as super::features::Title,
995 RenameAll,
996 Rename,
997 Example
998 ))
999 })
1000 .unwrap_or_default();
1001 let title = pop_feature!(unit_features => Feature::Title(_));
1002 let variant_name = rename_enum_variant(
1003 name.as_ref(),
1004 &mut unit_features,
1005 variant_rules,
1006 container_rules,
1007 rename_all,
1008 );
1009
1010 let example: Option<Feature> = pop_feature!(unit_features => Feature::Example(_));
1011
1012 let description =
1013 CommentAttributes::from_attributes(&variant.attrs).as_formatted_string();
1014 let description =
1015 (!description.is_empty()).then(|| Feature::Description(description.into()));
1016
1017 Enum::new([SimpleEnumVariant {
1019 value: variant_name
1020 .unwrap_or(Cow::Borrowed(&name))
1021 .to_token_stream(),
1022 }])
1023 .with_title(title.as_ref().map(ToTokens::to_token_stream))
1024 .with_example(example.as_ref().map(ToTokens::to_token_stream))
1025 .with_description(description.as_ref().map(ToTokens::to_token_stream))
1026 .to_token_stream()
1027 }
1028 }
1029 }
1030
1031 fn untagged_variant_tokens(&self, variant: &Variant) -> TokenStream {
1034 match &variant.fields {
1035 Fields::Named(named_fields) => {
1036 let mut named_struct_features = variant
1037 .attrs
1038 .parse_features::<EnumNamedFieldVariantFeatures>()
1039 .into_inner()
1040 .unwrap_or_default();
1041
1042 NamedStructSchema {
1043 struct_name: Cow::Borrowed(&*self.enum_name),
1044 attributes: &variant.attrs,
1045 rename_all: named_struct_features.pop_rename_all_feature(),
1046 features: Some(named_struct_features),
1047 fields: &named_fields.named,
1048 generics: None,
1049 aliases: None,
1050 schema_as: None,
1051 }
1052 .to_token_stream()
1053 }
1054 Fields::Unnamed(unnamed_fields) => {
1055 let unnamed_struct_features = variant
1056 .attrs
1057 .parse_features::<EnumUnnamedFieldVariantFeatures>()
1058 .into_inner()
1059 .unwrap_or_default();
1060
1061 UnnamedStructSchema {
1062 struct_name: Cow::Borrowed(&*self.enum_name),
1063 attributes: &variant.attrs,
1064 features: Some(unnamed_struct_features),
1065 fields: &unnamed_fields.unnamed,
1066 schema_as: None,
1067 }
1068 .to_token_stream()
1069 }
1070 Fields::Unit => {
1071 let mut unit_features =
1072 features::parse_schema_features_with(&variant.attrs, |input| {
1073 Ok(parse_features!(input as super::features::Title))
1074 })
1075 .unwrap_or_default();
1076 let title = pop_feature!(unit_features => Feature::Title(_));
1077
1078 UntaggedEnum::with_title(title).to_token_stream()
1079 }
1080 }
1081 }
1082
1083 fn tagged_variant_tokens(
1086 &self,
1087 tag: &str,
1088 name: Cow<'_, str>,
1089 variant: &Variant,
1090 variant_rules: &Option<SerdeValue>,
1091 container_rules: &Option<SerdeContainer>,
1092 rename_all: &Option<RenameAll>,
1093 ) -> TokenStream {
1094 match &variant.fields {
1095 Fields::Named(named_fields) => {
1096 let (title_features, mut named_struct_features) = variant
1097 .attrs
1098 .parse_features::<EnumNamedFieldVariantFeatures>()
1099 .into_inner()
1100 .map(|features| features.split_for_title())
1101 .unwrap_or_default();
1102 let variant_name = rename_enum_variant(
1103 name.as_ref(),
1104 &mut named_struct_features,
1105 variant_rules,
1106 container_rules,
1107 rename_all,
1108 );
1109
1110 let named_enum = NamedStructSchema {
1111 struct_name: Cow::Borrowed(&*self.enum_name),
1112 attributes: &variant.attrs,
1113 rename_all: named_struct_features.pop_rename_all_feature(),
1114 features: Some(named_struct_features),
1115 fields: &named_fields.named,
1116 generics: None,
1117 aliases: None,
1118 schema_as: None,
1119 };
1120 let title = title_features.first().map(ToTokens::to_token_stream);
1121
1122 let variant_name_tokens = Enum::new([SimpleEnumVariant {
1123 value: variant_name
1124 .unwrap_or(Cow::Borrowed(&name))
1125 .to_token_stream(),
1126 }]);
1127 quote! {
1128 #named_enum
1129 #title
1130 .property(#tag, #variant_name_tokens)
1131 .required(#tag)
1132 }
1133 }
1134 Fields::Unnamed(unnamed_fields) => {
1135 if unnamed_fields.unnamed.len() == 1 {
1136 let (title_features, mut unnamed_struct_features) = variant
1137 .attrs
1138 .parse_features::<EnumUnnamedFieldVariantFeatures>()
1139 .into_inner()
1140 .map(|features| features.split_for_title())
1141 .unwrap_or_default();
1142 let variant_name = rename_enum_variant(
1143 name.as_ref(),
1144 &mut unnamed_struct_features,
1145 variant_rules,
1146 container_rules,
1147 rename_all,
1148 );
1149
1150 let unnamed_enum = UnnamedStructSchema {
1151 struct_name: Cow::Borrowed(&*self.enum_name),
1152 attributes: &variant.attrs,
1153 features: Some(unnamed_struct_features),
1154 fields: &unnamed_fields.unnamed,
1155 schema_as: None,
1156 };
1157
1158 let title = title_features.first().map(ToTokens::to_token_stream);
1159 let variant_name_tokens = Enum::new([SimpleEnumVariant {
1160 value: variant_name
1161 .unwrap_or(Cow::Borrowed(&name))
1162 .to_token_stream(),
1163 }]);
1164
1165 let is_reference = unnamed_fields.unnamed.iter().any(|field| {
1166 let ty = TypeTree::from_type(&field.ty);
1167
1168 ty.value_type == ValueType::Object
1169 });
1170
1171 if is_reference {
1172 quote! {
1173 utoipa::openapi::schema::AllOfBuilder::new()
1174 #title
1175 .item(#unnamed_enum)
1176 .item(utoipa::openapi::schema::ObjectBuilder::new()
1177 .schema_type(utoipa::openapi::schema::SchemaType::Object)
1178 .property(#tag, #variant_name_tokens)
1179 .required(#tag)
1180 )
1181 }
1182 } else {
1183 quote! {
1184 #unnamed_enum
1185 #title
1186 .schema_type(utoipa::openapi::schema::SchemaType::Object)
1187 .property(#tag, #variant_name_tokens)
1188 .required(#tag)
1189 }
1190 }
1191 } else {
1192 abort!(
1193 variant,
1194 "Unnamed (tuple) enum variants are unsupported for internally tagged enums using the `tag = ` serde attribute";
1195
1196 help = "Try using a different serde enum representation";
1197 note = "See more about enum limitations here: `https://serde.rs/enum-representations.html#internally-tagged`"
1198 );
1199 }
1200 }
1201 Fields::Unit => {
1202 let mut unit_features =
1203 features::parse_schema_features_with(&variant.attrs, |input| {
1204 Ok(parse_features!(input as super::features::Title, Rename))
1205 })
1206 .unwrap_or_default();
1207 let title = pop_feature!(unit_features => Feature::Title(_));
1208
1209 let variant_name = rename_enum_variant(
1210 name.as_ref(),
1211 &mut unit_features,
1212 variant_rules,
1213 container_rules,
1214 rename_all,
1215 );
1216
1217 let variant_tokens = Enum::new([SimpleEnumVariant {
1219 value: variant_name
1220 .unwrap_or(Cow::Borrowed(&name))
1221 .to_token_stream(),
1222 }]);
1223
1224 quote! {
1225 utoipa::openapi::schema::ObjectBuilder::new()
1226 #title
1227 .property(#tag, #variant_tokens)
1228 .required(#tag)
1229 }
1230 }
1231 }
1232 }
1233
1234 #[allow(clippy::too_many_arguments)]
1236 fn adjacently_tagged_variant_tokens(
1237 &self,
1238 tag: &str,
1239 content: &str,
1240 name: Cow<'_, str>,
1241 variant: &Variant,
1242 variant_rules: &Option<SerdeValue>,
1243 container_rules: &Option<SerdeContainer>,
1244 rename_all: &Option<RenameAll>,
1245 ) -> TokenStream {
1246 match &variant.fields {
1247 Fields::Named(named_fields) => {
1248 let (title_features, mut named_struct_features) = variant
1249 .attrs
1250 .parse_features::<EnumNamedFieldVariantFeatures>()
1251 .into_inner()
1252 .map(|features| features.split_for_title())
1253 .unwrap_or_default();
1254 let variant_name = rename_enum_variant(
1255 name.as_ref(),
1256 &mut named_struct_features,
1257 variant_rules,
1258 container_rules,
1259 rename_all,
1260 );
1261
1262 let named_enum = NamedStructSchema {
1263 struct_name: Cow::Borrowed(&*self.enum_name),
1264 attributes: &variant.attrs,
1265 rename_all: named_struct_features.pop_rename_all_feature(),
1266 features: Some(named_struct_features),
1267 fields: &named_fields.named,
1268 generics: None,
1269 aliases: None,
1270 schema_as: None,
1271 };
1272 let title = title_features.first().map(ToTokens::to_token_stream);
1273
1274 let variant_name_tokens = Enum::new([SimpleEnumVariant {
1275 value: variant_name
1276 .unwrap_or(Cow::Borrowed(&name))
1277 .to_token_stream(),
1278 }]);
1279 quote! {
1280 utoipa::openapi::schema::ObjectBuilder::new()
1281 #title
1282 .schema_type(utoipa::openapi::schema::SchemaType::Object)
1283 .property(#tag, #variant_name_tokens)
1284 .required(#tag)
1285 .property(#content, #named_enum)
1286 .required(#content)
1287 }
1288 }
1289 Fields::Unnamed(unnamed_fields) => {
1290 if unnamed_fields.unnamed.len() == 1 {
1291 let (title_features, mut unnamed_struct_features) = variant
1292 .attrs
1293 .parse_features::<EnumUnnamedFieldVariantFeatures>()
1294 .into_inner()
1295 .map(|features| features.split_for_title())
1296 .unwrap_or_default();
1297 let variant_name = rename_enum_variant(
1298 name.as_ref(),
1299 &mut unnamed_struct_features,
1300 variant_rules,
1301 container_rules,
1302 rename_all,
1303 );
1304
1305 let unnamed_enum = UnnamedStructSchema {
1306 struct_name: Cow::Borrowed(&*self.enum_name),
1307 attributes: &variant.attrs,
1308 features: Some(unnamed_struct_features),
1309 fields: &unnamed_fields.unnamed,
1310 schema_as: None,
1311 };
1312
1313 let title = title_features.first().map(ToTokens::to_token_stream);
1314 let variant_name_tokens = Enum::new([SimpleEnumVariant {
1315 value: variant_name
1316 .unwrap_or(Cow::Borrowed(&name))
1317 .to_token_stream(),
1318 }]);
1319
1320 quote! {
1321 utoipa::openapi::schema::ObjectBuilder::new()
1322 #title
1323 .schema_type(utoipa::openapi::schema::SchemaType::Object)
1324 .property(#tag, #variant_name_tokens)
1325 .required(#tag)
1326 .property(#content, #unnamed_enum)
1327 .required(#content)
1328 }
1329 } else {
1330 abort!(
1331 variant,
1332 "Unnamed (tuple) enum variants are unsupported for adjacently tagged enums using the `tag = <tag>, content = <content>` serde attribute";
1333
1334 help = "Try using a different serde enum representation";
1335 note = "See more about enum limitations here: `https://serde.rs/enum-representations.html#adjacently-tagged`"
1336 );
1337 }
1338 }
1339 Fields::Unit => {
1340 let mut unit_features =
1343 features::parse_schema_features_with(&variant.attrs, |input| {
1344 Ok(parse_features!(input as super::features::Title, Rename))
1345 })
1346 .unwrap_or_default();
1347 let title = pop_feature!(unit_features => Feature::Title(_));
1348
1349 let variant_name = rename_enum_variant(
1350 name.as_ref(),
1351 &mut unit_features,
1352 variant_rules,
1353 container_rules,
1354 rename_all,
1355 );
1356
1357 let variant_tokens = Enum::new([SimpleEnumVariant {
1359 value: variant_name
1360 .unwrap_or(Cow::Borrowed(&name))
1361 .to_token_stream(),
1362 }]);
1363
1364 quote! {
1365 utoipa::openapi::schema::ObjectBuilder::new()
1366 #title
1367 .property(#tag, #variant_tokens)
1368 .required(#tag)
1369 }
1370 }
1371 }
1372 }
1373}
1374
1375impl ToTokens for ComplexEnum<'_> {
1376 fn to_tokens(&self, tokens: &mut TokenStream) {
1377 let attributes = &self.attributes;
1378 let container_rules = serde::parse_container(attributes);
1379
1380 let enum_repr = container_rules
1381 .as_ref()
1382 .map(|rules| rules.enum_repr.clone())
1383 .unwrap_or_default();
1384 let tag = match &enum_repr {
1385 SerdeEnumRepr::AdjacentlyTagged { tag, .. }
1386 | SerdeEnumRepr::InternallyTagged { tag } => Some(tag),
1387 SerdeEnumRepr::ExternallyTagged
1388 | SerdeEnumRepr::Untagged
1389 | SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => None,
1390 };
1391
1392 self.variants
1393 .iter()
1394 .filter_map(|variant: &Variant| {
1395 let variant_serde_rules = serde::parse_value(&variant.attrs);
1396 if is_not_skipped(&variant_serde_rules) {
1397 Some((variant, variant_serde_rules))
1398 } else {
1399 None
1400 }
1401 })
1402 .map(|(variant, variant_serde_rules)| {
1403 let variant_name = &*variant.ident.to_string();
1404
1405 match &enum_repr {
1406 SerdeEnumRepr::ExternallyTagged => self.variant_tokens(
1407 Cow::Borrowed(variant_name),
1408 variant,
1409 &variant_serde_rules,
1410 &container_rules,
1411 &self.rename_all,
1412 ),
1413 SerdeEnumRepr::InternallyTagged { tag } => self.tagged_variant_tokens(
1414 tag,
1415 Cow::Borrowed(variant_name),
1416 variant,
1417 &variant_serde_rules,
1418 &container_rules,
1419 &self.rename_all,
1420 ),
1421 SerdeEnumRepr::Untagged => self.untagged_variant_tokens(variant),
1422 SerdeEnumRepr::AdjacentlyTagged { tag, content } => self
1423 .adjacently_tagged_variant_tokens(
1424 tag,
1425 content,
1426 Cow::Borrowed(variant_name),
1427 variant,
1428 &variant_serde_rules,
1429 &container_rules,
1430 &self.rename_all,
1431 ),
1432 SerdeEnumRepr::UnfinishedAdjacentlyTagged { .. } => {
1433 unreachable!("Serde should not have parsed an UnfinishedAdjacentlyTagged")
1434 }
1435 }
1436 })
1437 .collect::<CustomEnum<'_, TokenStream>>()
1438 .with_discriminator(tag.map(|t| Cow::Borrowed(t.as_str())))
1439 .to_tokens(tokens);
1440
1441 tokens.extend(self.enum_features.to_token_stream());
1442 }
1443}
1444
1445#[cfg_attr(feature = "debug", derive(Debug))]
1446#[derive(PartialEq)]
1447struct TypeTuple<'a, T>(T, &'a Ident);
1448
1449#[cfg_attr(feature = "debug", derive(Debug))]
1450enum Property {
1451 Schema(ComponentSchema),
1452 SchemaWith(Feature),
1453}
1454
1455impl ToTokens for Property {
1456 fn to_tokens(&self, tokens: &mut TokenStream) {
1457 match self {
1458 Self::Schema(schema) => schema.to_tokens(tokens),
1459 Self::SchemaWith(schema_with) => schema_with.to_tokens(tokens),
1460 }
1461 }
1462}
1463
1464trait SchemaFeatureExt {
1465 fn split_for_title(self) -> (Vec<Feature>, Vec<Feature>);
1466}
1467
1468impl SchemaFeatureExt for Vec<Feature> {
1469 fn split_for_title(self) -> (Vec<Feature>, Vec<Feature>) {
1470 self.into_iter()
1471 .partition(|feature| matches!(feature, Feature::Title(_)))
1472 }
1473}
1474
1475pub(crate) fn format_path_ref(path: &Path) -> String {
1478 let mut path = path.clone();
1479
1480 if let Some(last_segment) = path.segments.last_mut() {
1482 last_segment.arguments = PathArguments::None;
1483 }
1484 path.to_token_stream().to_string().replace(" :: ", ".")
1487}
1488
1489#[inline]
1490fn is_not_skipped(rule: &Option<SerdeValue>) -> bool {
1491 rule.as_ref().map(|value| !value.skip).unwrap_or(true)
1492}
1493
1494#[inline]
1495fn is_flatten(rule: &Option<SerdeValue>) -> bool {
1496 rule.as_ref().map(|value| value.flatten).unwrap_or(false)
1497}
1498
1499#[cfg_attr(feature = "debug", derive(Debug))]
1500pub struct AliasSchema {
1501 pub name: String,
1502 pub ty: Type,
1503}
1504
1505impl AliasSchema {
1506 fn get_lifetimes(&self) -> impl Iterator<Item = &GenericArgument> {
1507 fn lifetimes_from_type(ty: &Type) -> impl Iterator<Item = &GenericArgument> {
1508 match ty {
1509 Type::Path(type_path) => type_path
1510 .path
1511 .segments
1512 .iter()
1513 .flat_map(|segment| match &segment.arguments {
1514 PathArguments::AngleBracketed(angle_bracketed_args) => {
1515 Some(angle_bracketed_args.args.iter())
1516 }
1517 _ => None,
1518 })
1519 .flatten()
1520 .flat_map(|arg| match arg {
1521 GenericArgument::Type(type_argument) => {
1522 lifetimes_from_type(type_argument).collect::<Vec<_>>()
1523 }
1524 _ => vec![arg],
1525 })
1526 .filter(|generic_arg| matches!(generic_arg, syn::GenericArgument::Lifetime(lifetime) if lifetime.ident != "'static")),
1527 _ => abort!(
1528 &ty.span(),
1529 "AliasSchema `get_lifetimes` only supports syn::TypePath types"
1530 ),
1531 }
1532 }
1533
1534 lifetimes_from_type(&self.ty)
1535 }
1536}
1537
1538impl Parse for AliasSchema {
1539 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
1540 let name = input.parse::<Ident>()?;
1541 input.parse::<Token![=]>()?;
1542
1543 Ok(Self {
1544 name: name.to_string(),
1545 ty: input.parse::<Type>()?,
1546 })
1547 }
1548}
1549
1550fn parse_aliases(attributes: &[Attribute]) -> Option<Punctuated<AliasSchema, Comma>> {
1551 attributes
1552 .iter()
1553 .find(|attribute| attribute.path().is_ident("aliases"))
1554 .map(|aliases| {
1555 aliases
1556 .parse_args_with(Punctuated::<AliasSchema, Comma>::parse_terminated)
1557 .unwrap_or_abort()
1558 })
1559}