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 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 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 #[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 #[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 pub fn to_schema_formatted_string(&self) -> String {
679 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
841impl_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
966impl_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 #[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
1029impl_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}