utoipa_gen/component/features/
attributes.rs

1use std::mem;
2
3use proc_macro2::{Ident, TokenStream};
4use quote::ToTokens;
5use syn::parse::ParseStream;
6use syn::punctuated::Punctuated;
7use syn::token::Paren;
8use syn::{Error, LitStr, Token, TypePath, WherePredicate};
9
10use crate::component::serde::RenameRule;
11use crate::component::{schema, GenericType, TypeTree};
12use crate::parse_utils::{LitBoolOrExprPath, LitStrOrExpr};
13use crate::path::parameter::{self, ParameterStyle};
14use crate::schema_type::KnownFormat;
15use crate::{parse_utils, AnyValue, Array, Diagnostics};
16
17use super::{impl_feature, Feature, Parse};
18use quote::quote;
19
20mod extensions;
21pub use extensions::Extensions;
22
23impl_feature! {
24    #[derive(Clone)]
25    #[cfg_attr(feature = "debug", derive(Debug))]
26    pub struct Default(pub(crate) Option<AnyValue>);
27}
28
29impl Default {
30    pub fn new_default_trait(struct_ident: Ident, field_ident: syn::Member) -> Self {
31        Self(Some(AnyValue::new_default_trait(struct_ident, field_ident)))
32    }
33}
34
35impl Parse for Default {
36    fn parse(input: syn::parse::ParseStream, _: proc_macro2::Ident) -> syn::Result<Self> {
37        if input.peek(syn::Token![=]) {
38            parse_utils::parse_next(input, || AnyValue::parse_any(input)).map(|any| Self(Some(any)))
39        } else {
40            Ok(Self(None))
41        }
42    }
43}
44
45impl ToTokens for Default {
46    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
47        match &self.0 {
48            Some(inner) => tokens.extend(quote! {Some(#inner)}),
49            None => tokens.extend(quote! {None}),
50        }
51    }
52}
53
54impl From<self::Default> for Feature {
55    fn from(value: self::Default) -> Self {
56        Feature::Default(value)
57    }
58}
59
60impl_feature! {
61    #[derive(Clone)]
62    #[cfg_attr(feature = "debug", derive(Debug))]
63    pub struct Example(AnyValue);
64}
65
66impl Parse for Example {
67    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
68        parse_utils::parse_next(input, || AnyValue::parse_any(input)).map(Self)
69    }
70}
71
72impl ToTokens for Example {
73    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
74        tokens.extend(self.0.to_token_stream())
75    }
76}
77
78impl From<Example> for Feature {
79    fn from(value: Example) -> Self {
80        Feature::Example(value)
81    }
82}
83
84impl_feature! {
85    #[derive(Clone)]
86    #[cfg_attr(feature = "debug", derive(Debug))]
87    pub struct Examples(Vec<AnyValue>);
88}
89
90impl Parse for Examples {
91    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
92    where
93        Self: std::marker::Sized,
94    {
95        let examples;
96        syn::parenthesized!(examples in input);
97
98        Ok(Self(
99            Punctuated::<AnyValue, Token![,]>::parse_terminated_with(
100                &examples,
101                AnyValue::parse_any,
102            )?
103            .into_iter()
104            .collect(),
105        ))
106    }
107}
108
109impl ToTokens for Examples {
110    fn to_tokens(&self, tokens: &mut TokenStream) {
111        if !self.0.is_empty() {
112            let examples = Array::Borrowed(&self.0).to_token_stream();
113            examples.to_tokens(tokens);
114        }
115    }
116}
117
118impl From<Examples> for Feature {
119    fn from(value: Examples) -> Self {
120        Feature::Examples(value)
121    }
122}
123
124impl_feature! {"xml" =>
125    #[derive(Default, Clone)]
126    #[cfg_attr(feature = "debug", derive(Debug))]
127    pub struct XmlAttr(schema::xml::XmlAttr);
128}
129
130impl XmlAttr {
131    /// Split [`XmlAttr`] for [`GenericType::Vec`] returning tuple of [`XmlAttr`]s where first
132    /// one is for a vec and second one is for object field.
133    pub fn split_for_vec(
134        &mut self,
135        type_tree: &TypeTree,
136    ) -> Result<(Option<XmlAttr>, Option<XmlAttr>), Diagnostics> {
137        if matches!(type_tree.generic_type, Some(GenericType::Vec)) {
138            let mut value_xml = mem::take(self);
139            let vec_xml = schema::xml::XmlAttr::with_wrapped(
140                mem::take(&mut value_xml.0.is_wrapped),
141                mem::take(&mut value_xml.0.wrap_name),
142            );
143
144            Ok((Some(XmlAttr(vec_xml)), Some(value_xml)))
145        } else {
146            self.validate_xml(&self.0)?;
147
148            Ok((None, Some(mem::take(self))))
149        }
150    }
151
152    #[inline]
153    fn validate_xml(&self, xml: &schema::xml::XmlAttr) -> Result<(), Diagnostics> {
154        if let Some(wrapped_ident) = xml.is_wrapped.as_ref() {
155            Err(Diagnostics::with_span(
156                wrapped_ident.span(),
157                "cannot use `wrapped` attribute in non slice field type",
158            )
159            .help("Try removing `wrapped` attribute or make your field `Vec`"))
160        } else {
161            Ok(())
162        }
163    }
164}
165
166impl Parse for XmlAttr {
167    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
168        let xml;
169        syn::parenthesized!(xml in input);
170        xml.parse::<schema::xml::XmlAttr>().map(Self)
171    }
172}
173
174impl ToTokens for XmlAttr {
175    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
176        tokens.extend(self.0.to_token_stream())
177    }
178}
179
180impl From<XmlAttr> for Feature {
181    fn from(value: XmlAttr) -> Self {
182        Feature::XmlAttr(value)
183    }
184}
185
186impl_feature! {
187    #[derive(Clone)]
188    #[cfg_attr(feature = "debug", derive(Debug))]
189    pub struct Format(KnownFormat);
190}
191
192impl Parse for Format {
193    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
194        parse_utils::parse_next(input, || input.parse::<KnownFormat>()).map(Self)
195    }
196}
197
198impl ToTokens for Format {
199    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
200        tokens.extend(self.0.to_token_stream())
201    }
202}
203
204impl From<Format> for Feature {
205    fn from(value: Format) -> Self {
206        Feature::Format(value)
207    }
208}
209
210impl_feature! {
211    #[derive(Clone, Copy)]
212    #[cfg_attr(feature = "debug", derive(Debug))]
213    pub struct WriteOnly(bool);
214}
215
216impl Parse for WriteOnly {
217    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
218        parse_utils::parse_bool_or_true(input).map(Self)
219    }
220}
221
222impl ToTokens for WriteOnly {
223    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
224        tokens.extend(self.0.to_token_stream())
225    }
226}
227
228impl From<WriteOnly> for Feature {
229    fn from(value: WriteOnly) -> Self {
230        Feature::WriteOnly(value)
231    }
232}
233
234impl_feature! {
235    #[derive(Clone, Copy)]
236    #[cfg_attr(feature = "debug", derive(Debug))]
237    pub struct ReadOnly(bool);
238}
239
240impl Parse for ReadOnly {
241    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
242        parse_utils::parse_bool_or_true(input).map(Self)
243    }
244}
245
246impl ToTokens for ReadOnly {
247    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
248        tokens.extend(self.0.to_token_stream())
249    }
250}
251
252impl From<ReadOnly> for Feature {
253    fn from(value: ReadOnly) -> Self {
254        Feature::ReadOnly(value)
255    }
256}
257
258impl_feature! {
259    #[derive(Clone)]
260    #[cfg_attr(feature = "debug", derive(Debug))]
261    pub struct Title(String);
262}
263
264impl Parse for Title {
265    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
266        parse_utils::parse_next_literal_str(input).map(Self)
267    }
268}
269
270impl ToTokens for Title {
271    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
272        tokens.extend(self.0.to_token_stream())
273    }
274}
275
276impl From<Title> for Feature {
277    fn from(value: Title) -> Self {
278        Feature::Title(value)
279    }
280}
281
282impl_feature! {
283    #[derive(Clone, Copy)]
284    #[cfg_attr(feature = "debug", derive(Debug))]
285    pub struct Nullable(bool);
286}
287
288impl Nullable {
289    pub fn new() -> Self {
290        Self(true)
291    }
292
293    pub fn value(&self) -> bool {
294        self.0
295    }
296
297    pub fn into_schema_type_token_stream(self) -> proc_macro2::TokenStream {
298        if self.0 {
299            quote! {utoipa::openapi::schema::Type::Null}
300        } else {
301            proc_macro2::TokenStream::new()
302        }
303    }
304}
305
306impl Parse for Nullable {
307    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
308        parse_utils::parse_bool_or_true(input).map(Self)
309    }
310}
311
312impl ToTokens for Nullable {
313    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
314        tokens.extend(self.0.to_token_stream())
315    }
316}
317
318impl From<Nullable> for Feature {
319    fn from(value: Nullable) -> Self {
320        Feature::Nullable(value)
321    }
322}
323
324impl_feature! {
325    #[cfg_attr(feature = "debug", derive(Debug))]
326    #[derive(Clone)]
327    pub struct Rename(String);
328}
329
330impl Rename {
331    pub fn into_value(self) -> String {
332        self.0
333    }
334}
335
336impl Parse for Rename {
337    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
338        parse_utils::parse_next_literal_str(input).map(Self)
339    }
340}
341
342impl ToTokens for Rename {
343    fn to_tokens(&self, tokens: &mut TokenStream) {
344        tokens.extend(self.0.to_token_stream())
345    }
346}
347
348impl From<Rename> for Feature {
349    fn from(value: Rename) -> Self {
350        Feature::Rename(value)
351    }
352}
353
354impl_feature! {
355    #[cfg_attr(feature = "debug", derive(Debug))]
356    #[derive(Clone)]
357    pub struct RenameAll(RenameRule);
358
359}
360impl RenameAll {
361    pub fn as_rename_rule(&self) -> &RenameRule {
362        &self.0
363    }
364}
365
366impl Parse for RenameAll {
367    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
368        let litstr = parse_utils::parse_next(input, || input.parse::<LitStr>())?;
369
370        litstr
371            .value()
372            .parse::<RenameRule>()
373            .map_err(|error| syn::Error::new(litstr.span(), error.to_string()))
374            .map(Self)
375    }
376}
377
378impl From<RenameAll> for Feature {
379    fn from(value: RenameAll) -> Self {
380        Feature::RenameAll(value)
381    }
382}
383
384impl_feature! {
385    #[cfg_attr(feature = "debug", derive(Debug))]
386    #[derive(Clone)]
387    pub struct Style(ParameterStyle);
388}
389
390impl From<ParameterStyle> for Style {
391    fn from(style: ParameterStyle) -> Self {
392        Self(style)
393    }
394}
395
396impl Parse for Style {
397    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
398        parse_utils::parse_next(input, || input.parse::<ParameterStyle>().map(Self))
399    }
400}
401
402impl ToTokens for Style {
403    fn to_tokens(&self, tokens: &mut TokenStream) {
404        self.0.to_tokens(tokens)
405    }
406}
407
408impl From<Style> for Feature {
409    fn from(value: Style) -> Self {
410        Feature::Style(value)
411    }
412}
413
414impl_feature! {
415    #[cfg_attr(feature = "debug", derive(Debug))]
416    #[derive(Clone)]
417    pub struct ParameterIn(parameter::ParameterIn);
418}
419
420impl ParameterIn {
421    pub fn is_query(&self) -> bool {
422        matches!(self.0, parameter::ParameterIn::Query)
423    }
424}
425
426impl Parse for ParameterIn {
427    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
428        parse_utils::parse_next(input, || input.parse::<parameter::ParameterIn>().map(Self))
429    }
430}
431
432impl ToTokens for ParameterIn {
433    fn to_tokens(&self, tokens: &mut TokenStream) {
434        self.0.to_tokens(tokens);
435    }
436}
437
438impl From<ParameterIn> for Feature {
439    fn from(value: ParameterIn) -> Self {
440        Feature::ParameterIn(value)
441    }
442}
443
444impl_feature! {
445    #[cfg_attr(feature = "debug", derive(Debug))]
446    #[derive(Clone)]
447    pub struct AllowReserved(bool);
448}
449
450impl Parse for AllowReserved {
451    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
452        parse_utils::parse_bool_or_true(input).map(Self)
453    }
454}
455
456impl ToTokens for AllowReserved {
457    fn to_tokens(&self, tokens: &mut TokenStream) {
458        self.0.to_tokens(tokens)
459    }
460}
461
462impl From<AllowReserved> for Feature {
463    fn from(value: AllowReserved) -> Self {
464        Feature::AllowReserved(value)
465    }
466}
467
468impl_feature! {
469    #[cfg_attr(feature = "debug", derive(Debug))]
470    #[derive(Clone)]
471    pub struct Explode(bool);
472}
473
474impl Parse for Explode {
475    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
476        parse_utils::parse_bool_or_true(input).map(Self)
477    }
478}
479
480impl ToTokens for Explode {
481    fn to_tokens(&self, tokens: &mut TokenStream) {
482        self.0.to_tokens(tokens)
483    }
484}
485
486impl From<Explode> for Feature {
487    fn from(value: Explode) -> Self {
488        Feature::Explode(value)
489    }
490}
491
492impl_feature! {
493    #[derive(Clone)]
494    #[cfg_attr(feature = "debug", derive(Debug))]
495    pub struct ValueType(syn::Type);
496}
497
498impl ValueType {
499    /// Create [`TypeTree`] from current [`syn::Type`].
500    pub fn as_type_tree(&self) -> Result<TypeTree, Diagnostics> {
501        TypeTree::from_type(&self.0)
502    }
503}
504
505impl Parse for ValueType {
506    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
507        parse_utils::parse_next(input, || input.parse::<syn::Type>()).map(Self)
508    }
509}
510
511impl From<ValueType> for Feature {
512    fn from(value: ValueType) -> Self {
513        Feature::ValueType(value)
514    }
515}
516
517impl_feature! {
518    #[derive(Clone)]
519    #[cfg_attr(feature = "debug", derive(Debug))]
520    pub struct Inline(pub(super) bool);
521}
522
523impl Parse for Inline {
524    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
525        parse_utils::parse_bool_or_true(input).map(Self)
526    }
527}
528
529impl From<bool> for Inline {
530    fn from(value: bool) -> Self {
531        Inline(value)
532    }
533}
534
535impl From<Inline> for Feature {
536    fn from(value: Inline) -> Self {
537        Feature::Inline(value)
538    }
539}
540
541impl_feature! {"names" =>
542    /// Specify names of unnamed fields with `names(...) attribute for `IntoParams` derive.
543    #[cfg_attr(feature = "debug", derive(Debug))]
544    #[derive(Clone)]
545    pub struct IntoParamsNames(Vec<String>);
546}
547
548impl IntoParamsNames {
549    pub fn into_values(self) -> Vec<String> {
550        self.0
551    }
552}
553
554impl Parse for IntoParamsNames {
555    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
556        Ok(Self(
557            parse_utils::parse_comma_separated_within_parenthesis::<LitStr>(input)?
558                .iter()
559                .map(LitStr::value)
560                .collect(),
561        ))
562    }
563}
564
565impl From<IntoParamsNames> for Feature {
566    fn from(value: IntoParamsNames) -> Self {
567        Feature::IntoParamsNames(value)
568    }
569}
570
571impl_feature! {
572    #[cfg_attr(feature = "debug", derive(Debug))]
573    #[derive(Clone)]
574    pub struct SchemaWith(TypePath);
575}
576
577impl Parse for SchemaWith {
578    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self> {
579        parse_utils::parse_next(input, || input.parse::<TypePath>().map(Self))
580    }
581}
582
583impl ToTokens for SchemaWith {
584    fn to_tokens(&self, tokens: &mut TokenStream) {
585        let path = &self.0;
586        tokens.extend(quote! {
587            #path()
588        })
589    }
590}
591
592impl From<SchemaWith> for Feature {
593    fn from(value: SchemaWith) -> Self {
594        Feature::SchemaWith(value)
595    }
596}
597
598impl_feature! {
599    #[cfg_attr(feature = "debug", derive(Debug))]
600    #[derive(Clone)]
601    pub struct Description(parse_utils::LitStrOrExpr);
602}
603
604impl Parse for Description {
605    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
606    where
607        Self: std::marker::Sized,
608    {
609        parse_utils::parse_next_literal_str_or_expr(input).map(Self)
610    }
611}
612
613impl ToTokens for Description {
614    fn to_tokens(&self, tokens: &mut TokenStream) {
615        self.0.to_tokens(tokens);
616    }
617}
618
619impl From<String> for Description {
620    fn from(value: String) -> Self {
621        Self(value.into())
622    }
623}
624
625impl From<Description> for Feature {
626    fn from(value: Description) -> Self {
627        Self::Description(value)
628    }
629}
630
631impl_feature! {
632    /// Deprecated feature parsed from macro attributes.
633    ///
634    /// This feature supports only syntax parsed from utoipa specific macro attributes, it does not
635    /// support Rust `#[deprecated]` attribute.
636    #[cfg_attr(feature = "debug", derive(Debug))]
637    #[derive(Clone)]
638    pub struct Deprecated(bool);
639}
640
641impl Parse for Deprecated {
642    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
643    where
644        Self: std::marker::Sized,
645    {
646        parse_utils::parse_bool_or_true(input).map(Self)
647    }
648}
649
650impl ToTokens for Deprecated {
651    fn to_tokens(&self, tokens: &mut TokenStream) {
652        let deprecated: crate::Deprecated = self.0.into();
653        deprecated.to_tokens(tokens);
654    }
655}
656
657impl From<Deprecated> for Feature {
658    fn from(value: Deprecated) -> Self {
659        Self::Deprecated(value)
660    }
661}
662
663impl From<bool> for Deprecated {
664    fn from(value: bool) -> Self {
665        Self(value)
666    }
667}
668
669impl_feature! {
670    #[cfg_attr(feature = "debug", derive(Debug))]
671    #[derive(Clone)]
672    pub struct As(pub TypePath);
673}
674
675impl As {
676    /// Returns this `As` attribute type path formatted as string supported by OpenAPI spec whereas
677    /// double colons (::) are replaced with dot (.).
678    pub fn to_schema_formatted_string(&self) -> String {
679        // See: https://github.com/juhaku/utoipa/pull/187#issuecomment-1173101405
680        // :: are not officially supported in the spec
681        self.0
682            .path
683            .segments
684            .iter()
685            .map(|segment| segment.ident.to_string())
686            .collect::<Vec<_>>()
687            .join(".")
688    }
689}
690
691impl Parse for As {
692    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
693    where
694        Self: std::marker::Sized,
695    {
696        parse_utils::parse_next(input, || input.parse()).map(Self)
697    }
698}
699
700impl From<As> for Feature {
701    fn from(value: As) -> Self {
702        Self::As(value)
703    }
704}
705
706impl_feature! {
707    #[cfg_attr(feature = "debug", derive(Debug))]
708    #[derive(Clone)]
709    pub struct AdditionalProperties(bool);
710}
711
712impl Parse for AdditionalProperties {
713    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
714    where
715        Self: std::marker::Sized,
716    {
717        parse_utils::parse_bool_or_true(input).map(Self)
718    }
719}
720
721impl ToTokens for AdditionalProperties {
722    fn to_tokens(&self, tokens: &mut TokenStream) {
723        let additional_properties = &self.0;
724        tokens.extend(quote!(
725            utoipa::openapi::schema::AdditionalProperties::FreeForm(
726                #additional_properties
727            )
728        ))
729    }
730}
731
732impl From<AdditionalProperties> for Feature {
733    fn from(value: AdditionalProperties) -> Self {
734        Self::AdditionalProperties(value)
735    }
736}
737
738impl_feature! {
739    #[derive(Clone)]
740    #[cfg_attr(feature = "debug", derive(Debug))]
741    pub struct Required(pub bool);
742}
743
744impl Required {
745    pub fn is_true(&self) -> bool {
746        self.0
747    }
748}
749
750impl Parse for Required {
751    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
752    where
753        Self: std::marker::Sized,
754    {
755        parse_utils::parse_bool_or_true(input).map(Self)
756    }
757}
758
759impl ToTokens for Required {
760    fn to_tokens(&self, tokens: &mut TokenStream) {
761        self.0.to_tokens(tokens)
762    }
763}
764
765impl From<crate::Required> for Required {
766    fn from(value: crate::Required) -> Self {
767        if value == crate::Required::True {
768            Self(true)
769        } else {
770            Self(false)
771        }
772    }
773}
774
775impl From<bool> for Required {
776    fn from(value: bool) -> Self {
777        Self(value)
778    }
779}
780
781impl From<Required> for Feature {
782    fn from(value: Required) -> Self {
783        Self::Required(value)
784    }
785}
786
787impl_feature! {
788    #[derive(Clone)]
789    #[cfg_attr(feature = "debug", derive(Debug))]
790    pub struct ContentEncoding(String);
791}
792
793impl Parse for ContentEncoding {
794    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
795    where
796        Self: std::marker::Sized,
797    {
798        parse_utils::parse_next_literal_str(input).map(Self)
799    }
800}
801
802impl ToTokens for ContentEncoding {
803    fn to_tokens(&self, tokens: &mut TokenStream) {
804        self.0.to_tokens(tokens);
805    }
806}
807
808impl From<ContentEncoding> for Feature {
809    fn from(value: ContentEncoding) -> Self {
810        Self::ContentEncoding(value)
811    }
812}
813
814impl_feature! {
815    #[derive(Clone)]
816    #[cfg_attr(feature = "debug", derive(Debug))]
817    pub struct ContentMediaType(String);
818}
819
820impl Parse for ContentMediaType {
821    fn parse(input: ParseStream, _: Ident) -> syn::Result<Self>
822    where
823        Self: std::marker::Sized,
824    {
825        parse_utils::parse_next_literal_str(input).map(Self)
826    }
827}
828
829impl ToTokens for ContentMediaType {
830    fn to_tokens(&self, tokens: &mut TokenStream) {
831        self.0.to_tokens(tokens);
832    }
833}
834
835impl From<ContentMediaType> for Feature {
836    fn from(value: ContentMediaType) -> Self {
837        Self::ContentMediaType(value)
838    }
839}
840
841// discriminator = ...
842// discriminator(property_name = ..., mapping(
843//      (value = ...),
844//      (value2 = ...)
845// ))
846impl_feature! {
847    #[derive(Clone)]
848    #[cfg_attr(feature = "debug", derive(Debug))]
849    pub struct Discriminator(LitStrOrExpr, Punctuated<(LitStrOrExpr, LitStrOrExpr), Token![,]>, Ident);
850}
851
852impl Discriminator {
853    fn new(attribute: Ident) -> Self {
854        Self(LitStrOrExpr::default(), Punctuated::default(), attribute)
855    }
856
857    pub fn get_attribute(&self) -> &Ident {
858        &self.2
859    }
860}
861
862impl Parse for Discriminator {
863    fn parse(input: ParseStream, attribute: Ident) -> syn::Result<Self>
864    where
865        Self: std::marker::Sized,
866    {
867        let lookahead = input.lookahead1();
868        if lookahead.peek(Token![=]) {
869            parse_utils::parse_next_literal_str_or_expr(input)
870                .map(|property_name| Self(property_name, Punctuated::new(), attribute))
871        } else if lookahead.peek(Paren) {
872            let discriminator_stream;
873            syn::parenthesized!(discriminator_stream in input);
874
875            let mut discriminator = Discriminator::new(attribute);
876
877            while !discriminator_stream.is_empty() {
878                let property = discriminator_stream.parse::<Ident>()?;
879                let name = &*property.to_string();
880
881                match name {
882                    "property_name" => {
883                        discriminator.0 =
884                            parse_utils::parse_next_literal_str_or_expr(&discriminator_stream)?
885                    }
886                    "mapping" => {
887                        let mapping_stream;
888                        syn::parenthesized!(mapping_stream in &discriminator_stream);
889                        let mappings: Punctuated<(LitStrOrExpr, LitStrOrExpr), Token![,]> =
890                            Punctuated::parse_terminated_with(&mapping_stream, |input| {
891                                let inner;
892                                syn::parenthesized!(inner in input);
893
894                                let key = inner.parse::<LitStrOrExpr>()?;
895                                inner.parse::<Token![=]>()?;
896                                let value = inner.parse::<LitStrOrExpr>()?;
897
898                                Ok((key, value))
899                            })?;
900                        discriminator.1 = mappings;
901                    }
902                    unexpected => {
903                        return Err(Error::new(
904                            property.span(),
905                            format!(
906                                "unexpected identifier {}, expected any of: property_name, mapping",
907                                unexpected
908                            ),
909                        ))
910                    }
911                }
912
913                if !discriminator_stream.is_empty() {
914                    discriminator_stream.parse::<Token![,]>()?;
915                }
916            }
917
918            Ok(discriminator)
919        } else {
920            Err(lookahead.error())
921        }
922    }
923}
924
925impl ToTokens for Discriminator {
926    fn to_tokens(&self, tokens: &mut TokenStream) {
927        let Discriminator(property_name, mapping, _) = self;
928
929        struct Mapping<'m>(&'m LitStrOrExpr, &'m LitStrOrExpr);
930
931        impl ToTokens for Mapping<'_> {
932            fn to_tokens(&self, tokens: &mut TokenStream) {
933                let Mapping(property_name, value) = *self;
934
935                tokens.extend(quote! {
936                    (#property_name, #value)
937                })
938            }
939        }
940
941        let discriminator = if !mapping.is_empty() {
942            let mapping = mapping
943                .iter()
944                .map(|(key, value)| Mapping(key, value))
945                .collect::<Array<Mapping>>();
946
947            quote! {
948                utoipa::openapi::schema::Discriminator::with_mapping(#property_name, #mapping)
949            }
950        } else {
951            quote! {
952                utoipa::openapi::schema::Discriminator::new(#property_name)
953            }
954        };
955
956        discriminator.to_tokens(tokens);
957    }
958}
959
960impl From<Discriminator> for Feature {
961    fn from(value: Discriminator) -> Self {
962        Self::Discriminator(value)
963    }
964}
965
966// bound = "GenericTy: Trait"
967impl_feature! {
968    #[derive(Clone)]
969    #[cfg_attr(feature = "debug", derive(Debug))]
970    pub struct Bound(pub(crate) Punctuated<WherePredicate, Token![,]>);
971}
972
973impl Parse for Bound {
974    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self> {
975        let litstr = parse_utils::parse_next(input, || input.parse::<LitStr>())?;
976        let bounds =
977            syn::parse::Parser::parse_str(<Punctuated<_, _>>::parse_terminated, &litstr.value())
978                .map_err(|err| syn::Error::new(litstr.span(), err.to_string()))?;
979        Ok(Self(bounds))
980    }
981}
982
983impl ToTokens for Bound {
984    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
985        tokens.extend(self.0.to_token_stream())
986    }
987}
988
989impl From<Bound> for Feature {
990    fn from(value: Bound) -> Self {
991        Feature::Bound(value)
992    }
993}
994
995impl_feature! {
996    /// Ignore feature parsed from macro attributes.
997    #[derive(Clone)]
998    #[cfg_attr(feature = "debug", derive(Debug))]
999    pub struct Ignore(pub LitBoolOrExprPath);
1000}
1001
1002impl Parse for Ignore {
1003    fn parse(input: syn::parse::ParseStream, _: Ident) -> syn::Result<Self>
1004    where
1005        Self: std::marker::Sized,
1006    {
1007        parse_utils::parse_next_literal_bool_or_call(input).map(Self)
1008    }
1009}
1010
1011impl ToTokens for Ignore {
1012    fn to_tokens(&self, tokens: &mut proc_macro2::TokenStream) {
1013        tokens.extend(self.0.to_token_stream())
1014    }
1015}
1016
1017impl From<Ignore> for Feature {
1018    fn from(value: Ignore) -> Self {
1019        Self::Ignore(value)
1020    }
1021}
1022
1023impl From<bool> for Ignore {
1024    fn from(value: bool) -> Self {
1025        Self(value.into())
1026    }
1027}
1028
1029// Nothing to parse, it is considered to be set when attribute itself is parsed via
1030// `parse_features!`.
1031impl_feature! {
1032    #[derive(Clone)]
1033    #[cfg_attr(feature = "debug", derive(Debug))]
1034    pub struct NoRecursion;
1035}
1036
1037impl Parse for NoRecursion {
1038    fn parse(_: ParseStream, _: Ident) -> syn::Result<Self>
1039    where
1040        Self: std::marker::Sized,
1041    {
1042        Ok(Self)
1043    }
1044}
1045
1046impl From<NoRecursion> for Feature {
1047    fn from(value: NoRecursion) -> Self {
1048        Self::NoRecursion(value)
1049    }
1050}