utoipa_gen/path/response/
derive.rs

1use std::borrow::Cow;
2use std::{iter, mem};
3
4use proc_macro2::{Ident, Span, TokenStream};
5use quote::{quote, ToTokens};
6use syn::parse::ParseStream;
7use syn::punctuated::Punctuated;
8use syn::spanned::Spanned;
9use syn::token::Comma;
10use syn::{
11    Attribute, Data, Field, Fields, Generics, Lifetime, LifetimeParam, LitStr, Path, Type,
12    TypePath, Variant,
13};
14
15use crate::component::schema::{EnumSchema, NamedStructSchema, Root};
16use crate::doc_comment::CommentAttributes;
17use crate::path::media_type::{DefaultSchema, MediaTypeAttr, ParsedType, Schema};
18use crate::{
19    as_tokens_or_diagnostics, parse_utils, Array, Diagnostics, OptionExt, ToTokensDiagnostics,
20};
21
22use super::{
23    DeriveIntoResponsesValue, DeriveResponseValue, DeriveToResponseValue, ResponseTuple,
24    ResponseTupleInner, ResponseValue,
25};
26
27pub struct ToResponse<'r> {
28    ident: Ident,
29    lifetime: Lifetime,
30    generics: Generics,
31    response: ResponseTuple<'r>,
32}
33
34impl<'r> ToResponse<'r> {
35    const LIFETIME: &'static str = "'__r";
36
37    pub fn new(
38        attributes: Vec<Attribute>,
39        data: &'r Data,
40        generics: Generics,
41        ident: Ident,
42    ) -> Result<ToResponse<'r>, Diagnostics> {
43        let response = match &data {
44            Data::Struct(struct_value) => match &struct_value.fields {
45                Fields::Named(fields) => {
46                    ToResponseNamedStructResponse::new(&attributes, &ident, &fields.named)?.0
47                }
48                Fields::Unnamed(fields) => {
49                    let field = fields
50                        .unnamed
51                        .iter()
52                        .next()
53                        .expect("Unnamed struct must have 1 field");
54
55                    ToResponseUnnamedStructResponse::new(&attributes, &field.ty, &field.attrs)?.0
56                }
57                Fields::Unit => ToResponseUnitStructResponse::new(&attributes)?.0,
58            },
59            Data::Enum(enum_value) => {
60                EnumResponse::new(&ident, &enum_value.variants, &attributes)?.0
61            }
62            Data::Union(_) => {
63                return Err(Diagnostics::with_span(
64                    ident.span(),
65                    "`ToResponse` does not support `Union` type",
66                ))
67            }
68        };
69
70        let lifetime = Lifetime::new(ToResponse::LIFETIME, Span::call_site());
71
72        Ok(Self {
73            ident,
74            lifetime,
75            generics,
76            response,
77        })
78    }
79}
80
81impl ToTokensDiagnostics for ToResponse<'_> {
82    fn to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostics> {
83        let (_, ty_generics, where_clause) = self.generics.split_for_impl();
84
85        let lifetime = &self.lifetime;
86        let ident = &self.ident;
87        let name = ident.to_string();
88        let response = as_tokens_or_diagnostics!(&self.response);
89
90        let mut to_response_generics = self.generics.clone();
91        to_response_generics
92            .params
93            .push(syn::GenericParam::Lifetime(LifetimeParam::new(
94                lifetime.clone(),
95            )));
96        let (to_response_impl_generics, _, _) = to_response_generics.split_for_impl();
97
98        tokens.extend(quote! {
99            impl #to_response_impl_generics utoipa::ToResponse <#lifetime> for #ident #ty_generics #where_clause {
100                fn response() -> (& #lifetime str, utoipa::openapi::RefOr<utoipa::openapi::response::Response>) {
101                    (#name, #response.into())
102                }
103            }
104        });
105
106        Ok(())
107    }
108}
109
110pub struct IntoResponses {
111    pub attributes: Vec<Attribute>,
112    pub data: Data,
113    pub generics: Generics,
114    pub ident: Ident,
115}
116
117impl ToTokensDiagnostics for IntoResponses {
118    fn to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostics> {
119        let responses = match &self.data {
120            Data::Struct(struct_value) => match &struct_value.fields {
121                Fields::Named(fields) => {
122                    let response =
123                        NamedStructResponse::new(&self.attributes, &self.ident, &fields.named)?.0;
124                    let status = &response.status_code;
125                    let response_tokens = as_tokens_or_diagnostics!(&response);
126
127                    Array::from_iter(iter::once(quote!((#status, #response_tokens))))
128                }
129                Fields::Unnamed(fields) => {
130                    let field = fields
131                        .unnamed
132                        .iter()
133                        .next()
134                        .expect("Unnamed struct must have 1 field");
135
136                    let response =
137                        UnnamedStructResponse::new(&self.attributes, &field.ty, &field.attrs)?.0;
138                    let status = &response.status_code;
139                    let response_tokens = as_tokens_or_diagnostics!(&response);
140
141                    Array::from_iter(iter::once(quote!((#status, #response_tokens))))
142                }
143                Fields::Unit => {
144                    let response = UnitStructResponse::new(&self.attributes)?.0;
145                    let status = &response.status_code;
146                    let response_tokens = as_tokens_or_diagnostics!(&response);
147
148                    Array::from_iter(iter::once(quote!((#status, #response_tokens))))
149                }
150            },
151            Data::Enum(enum_value) => enum_value
152                .variants
153                .iter()
154                .map(|variant| match &variant.fields {
155                    Fields::Named(fields) => Ok(NamedStructResponse::new(
156                        &variant.attrs,
157                        &variant.ident,
158                        &fields.named,
159                    )?
160                    .0),
161                    Fields::Unnamed(fields) => {
162                        let field = fields
163                            .unnamed
164                            .iter()
165                            .next()
166                            .expect("Unnamed enum variant must have 1 field");
167                        match UnnamedStructResponse::new(&variant.attrs, &field.ty, &field.attrs) {
168                            Ok(response) => Ok(response.0),
169                            Err(diagnostics) => Err(diagnostics),
170                        }
171                    }
172                    Fields::Unit => Ok(UnitStructResponse::new(&variant.attrs)?.0),
173                })
174                .collect::<Result<Vec<ResponseTuple>, Diagnostics>>()?
175                .iter()
176                .map(|response| {
177                    let status = &response.status_code;
178                    let response_tokens = as_tokens_or_diagnostics!(response);
179                    Ok(quote!((#status, utoipa::openapi::RefOr::from(#response_tokens))))
180                })
181                .collect::<Result<Array<TokenStream>, Diagnostics>>()?,
182            Data::Union(_) => {
183                return Err(Diagnostics::with_span(
184                    self.ident.span(),
185                    "`IntoResponses` does not support `Union` type",
186                ))
187            }
188        };
189
190        let ident = &self.ident;
191        let (impl_generics, ty_generics, where_clause) = self.generics.split_for_impl();
192
193        let responses = if responses.len() > 0 {
194            Some(quote!( .responses_from_iter(#responses)))
195        } else {
196            None
197        };
198        tokens.extend(quote!{
199                impl #impl_generics utoipa::IntoResponses for #ident #ty_generics #where_clause {
200                    fn responses() -> std::collections::BTreeMap<String, utoipa::openapi::RefOr<utoipa::openapi::response::Response>> {
201                        utoipa::openapi::response::ResponsesBuilder::new()
202                            #responses
203                            .build()
204                            .into()
205                    }
206                }
207            });
208
209        Ok(())
210    }
211}
212
213trait Response {
214    fn to_type(ident: &Ident) -> Type {
215        let path = Path::from(ident.clone());
216        let type_path = TypePath { path, qself: None };
217        Type::Path(type_path)
218    }
219
220    fn has_no_field_attributes(attribute: &Attribute) -> (bool, &'static str) {
221        const ERROR: &str =
222            "Unexpected field attribute, field attributes are only supported at unnamed fields";
223
224        let ident = attribute.path().get_ident().unwrap();
225        match &*ident.to_string() {
226            "to_schema" => (false, ERROR),
227            "ref_response" => (false, ERROR),
228            "content" => (false, ERROR),
229            "to_response" => (false, ERROR),
230            _ => (true, ERROR),
231        }
232    }
233
234    fn validate_attributes<'a, I: IntoIterator<Item = &'a Attribute>>(
235        attributes: I,
236        validate: impl Fn(&Attribute) -> (bool, &'static str) + 'a,
237    ) -> impl Iterator<Item = Diagnostics> {
238        attributes.into_iter().filter_map(move |attribute| {
239            let (valid, error_message) = validate(attribute);
240            if !valid {
241                Some(Diagnostics::with_span(attribute.span(), error_message))
242            } else {
243                None
244            }
245        })
246    }
247}
248
249struct UnnamedStructResponse<'u>(ResponseTuple<'u>);
250
251impl Response for UnnamedStructResponse<'_> {}
252
253impl<'u> UnnamedStructResponse<'u> {
254    fn new(
255        attributes: &[Attribute],
256        ty: &'u Type,
257        inner_attributes: &[Attribute],
258    ) -> Result<Self, Diagnostics> {
259        let is_inline = inner_attributes
260            .iter()
261            .any(|attribute| attribute.path().get_ident().unwrap() == "to_schema");
262        let ref_response = inner_attributes
263            .iter()
264            .any(|attribute| attribute.path().get_ident().unwrap() == "ref_response");
265        let to_response = inner_attributes
266            .iter()
267            .any(|attribute| attribute.path().get_ident().unwrap() == "to_response");
268
269        if is_inline && (ref_response || to_response) {
270            return Err(Diagnostics::with_span(ty.span(), "Attribute `to_schema` cannot be used with `ref_response` and `to_response` attribute"));
271        }
272        let mut derive_value = DeriveIntoResponsesValue::from_attributes(attributes)?
273            .expect("`IntoResponses` must have `#[response(...)]` attribute");
274        let description = {
275            let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
276            parse_utils::LitStrOrExpr::LitStr(LitStr::new(&s, Span::call_site()))
277        };
278        let status_code = mem::take(&mut derive_value.status);
279
280        let response = match (ref_response, to_response) {
281            (false, false) => Self(
282                (
283                    status_code,
284                    ResponseValue::from_derive_into_responses_value(
285                        derive_value,
286                        ParsedType {
287                            ty: Cow::Borrowed(ty),
288                            is_inline,
289                        },
290                        description,
291                    ),
292                )
293                    .into(),
294            ),
295            (true, false) => Self(ResponseTuple {
296                inner: Some(ResponseTupleInner::Ref(ParsedType {
297                    ty: Cow::Borrowed(ty),
298                    is_inline: false,
299                })),
300                status_code,
301            }),
302            (false, true) => Self(ResponseTuple {
303                inner: Some(ResponseTupleInner::Ref(ParsedType {
304                    ty: Cow::Borrowed(ty),
305                    is_inline: true,
306                })),
307                status_code,
308            }),
309            (true, true) => {
310                return Err(Diagnostics::with_span(
311                    ty.span(),
312                    "Cannot define `ref_response` and `to_response` attribute simultaneously",
313                ))
314            }
315        };
316
317        Ok(response)
318    }
319}
320
321struct NamedStructResponse<'n>(ResponseTuple<'n>);
322
323impl Response for NamedStructResponse<'_> {}
324
325impl NamedStructResponse<'_> {
326    fn new(
327        attributes: &[Attribute],
328        ident: &Ident,
329        fields: &Punctuated<Field, Comma>,
330    ) -> Result<Self, Diagnostics> {
331        if let Some(diagnostics) =
332            Self::validate_attributes(attributes, Self::has_no_field_attributes)
333                .chain(Self::validate_attributes(
334                    fields.iter().flat_map(|field| &field.attrs),
335                    Self::has_no_field_attributes,
336                ))
337                .collect::<Option<Diagnostics>>()
338        {
339            return Err(diagnostics);
340        }
341
342        let mut derive_value = DeriveIntoResponsesValue::from_attributes(attributes)?
343            .expect("`IntoResponses` must have `#[response(...)]` attribute");
344        let description = {
345            let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
346            parse_utils::LitStrOrExpr::LitStr(LitStr::new(&s, Span::call_site()))
347        };
348        let status_code = mem::take(&mut derive_value.status);
349        let inline_schema = NamedStructSchema::new(
350            &Root {
351                ident,
352                attributes,
353                generics: &Generics::default(),
354            },
355            fields,
356            Vec::new(),
357        )?;
358
359        let ty = Self::to_type(ident);
360
361        Ok(Self(
362            (
363                status_code,
364                ResponseValue::from_derive_into_responses_value(
365                    derive_value,
366                    Schema::Default(DefaultSchema::Raw {
367                        tokens: inline_schema.to_token_stream(),
368                        ty: Cow::Owned(ty),
369                    }),
370                    description,
371                ),
372            )
373                .into(),
374        ))
375    }
376}
377
378struct UnitStructResponse<'u>(ResponseTuple<'u>);
379
380impl Response for UnitStructResponse<'_> {}
381
382impl UnitStructResponse<'_> {
383    fn new(attributes: &[Attribute]) -> Result<Self, Diagnostics> {
384        if let Some(diagnostics) =
385            Self::validate_attributes(attributes, Self::has_no_field_attributes)
386                .collect::<Option<Diagnostics>>()
387        {
388            return Err(diagnostics);
389        }
390
391        let mut derive_value = DeriveIntoResponsesValue::from_attributes(attributes)?
392            .expect("`IntoResponses` must have `#[response(...)]` attribute");
393        let status_code = mem::take(&mut derive_value.status);
394        let description = {
395            let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
396            parse_utils::LitStrOrExpr::LitStr(LitStr::new(&s, Span::call_site()))
397        };
398
399        Ok(Self(
400            (
401                status_code,
402                ResponseValue::from_derive_into_responses_value(
403                    derive_value,
404                    Schema::Default(DefaultSchema::None),
405                    description,
406                ),
407            )
408                .into(),
409        ))
410    }
411}
412
413struct ToResponseNamedStructResponse<'p>(ResponseTuple<'p>);
414
415impl Response for ToResponseNamedStructResponse<'_> {}
416
417impl<'p> ToResponseNamedStructResponse<'p> {
418    fn new(
419        attributes: &[Attribute],
420        ident: &Ident,
421        fields: &Punctuated<Field, Comma>,
422    ) -> Result<Self, Diagnostics> {
423        if let Some(diagnostics) =
424            Self::validate_attributes(attributes, Self::has_no_field_attributes)
425                .chain(Self::validate_attributes(
426                    fields.iter().flat_map(|field| &field.attrs),
427                    Self::has_no_field_attributes,
428                ))
429                .collect::<Option<Diagnostics>>()
430        {
431            return Err(diagnostics);
432        }
433
434        let derive_value = DeriveToResponseValue::from_attributes(attributes)?;
435        let description = {
436            let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
437            parse_utils::LitStrOrExpr::LitStr(LitStr::new(&s, Span::call_site()))
438        };
439        let ty = Self::to_type(ident);
440
441        let inline_schema = NamedStructSchema::new(
442            &Root {
443                ident,
444                attributes,
445                generics: &Generics::default(),
446            },
447            fields,
448            Vec::new(),
449        )?;
450
451        let response_value = if let Some(derive_value) = derive_value {
452            ResponseValue::from_derive_to_response_value(
453                derive_value,
454                Schema::Default(DefaultSchema::Raw {
455                    tokens: inline_schema.to_token_stream(),
456                    ty: Cow::Owned(ty),
457                }),
458                description,
459            )
460        } else {
461            ResponseValue::from_schema(
462                Schema::Default(DefaultSchema::Raw {
463                    tokens: inline_schema.to_token_stream(),
464                    ty: Cow::Owned(ty),
465                }),
466                description,
467            )
468        };
469        // response_value.response_type = Some(response_type);
470
471        Ok(Self(response_value.into()))
472    }
473}
474
475struct ToResponseUnnamedStructResponse<'c>(ResponseTuple<'c>);
476
477impl Response for ToResponseUnnamedStructResponse<'_> {}
478
479impl<'u> ToResponseUnnamedStructResponse<'u> {
480    fn new(
481        attributes: &[Attribute],
482        ty: &'u Type,
483        inner_attributes: &[Attribute],
484    ) -> Result<Self, Diagnostics> {
485        if let Some(diagnostics) =
486            Self::validate_attributes(attributes, Self::has_no_field_attributes)
487                .chain(Self::validate_attributes(inner_attributes, |attribute| {
488                    const ERROR: &str =
489                "Unexpected attribute, `content` is only supported on unnamed field enum variant";
490                    if attribute.path().get_ident().unwrap() == "content" {
491                        (false, ERROR)
492                    } else {
493                        (true, ERROR)
494                    }
495                }))
496                .collect::<Option<Diagnostics>>()
497        {
498            return Err(diagnostics);
499        }
500        let derive_value = DeriveToResponseValue::from_attributes(attributes)?;
501        let description = {
502            let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
503            parse_utils::LitStrOrExpr::LitStr(LitStr::new(&s, Span::call_site()))
504        };
505
506        let is_inline = inner_attributes
507            .iter()
508            .any(|attribute| attribute.path().get_ident().unwrap() == "to_schema");
509
510        let response_value = if let Some(derive_value) = derive_value {
511            ResponseValue::from_derive_to_response_value(
512                derive_value,
513                ParsedType {
514                    ty: Cow::Borrowed(ty),
515                    is_inline,
516                },
517                description,
518            )
519        } else {
520            ResponseValue::from_schema(
521                ParsedType {
522                    ty: Cow::Borrowed(ty),
523                    is_inline,
524                },
525                description,
526            )
527        };
528
529        Ok(Self(response_value.into()))
530    }
531}
532
533#[cfg_attr(feature = "debug", derive(Debug))]
534struct VariantAttributes<'r> {
535    type_and_content: Option<(&'r Type, String)>,
536    derive_value: Option<DeriveToResponseValue>,
537    is_inline: bool,
538}
539
540struct EnumResponse<'r>(ResponseTuple<'r>);
541
542impl Response for EnumResponse<'_> {}
543
544impl<'r> EnumResponse<'r> {
545    fn new(
546        ident: &Ident,
547        variants: &'r Punctuated<Variant, Comma>,
548        attributes: &[Attribute],
549    ) -> Result<Self, Diagnostics> {
550        if let Some(diagnostics) =
551            Self::validate_attributes(attributes, Self::has_no_field_attributes)
552                .chain(Self::validate_attributes(
553                    variants.iter().flat_map(|variant| &variant.attrs),
554                    Self::has_no_field_attributes,
555                ))
556                .collect::<Option<Diagnostics>>()
557        {
558            return Err(diagnostics);
559        }
560
561        let ty = Self::to_type(ident);
562        let description = {
563            let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
564            parse_utils::LitStrOrExpr::LitStr(LitStr::new(&s, Span::call_site()))
565        };
566
567        let content = variants
568            .into_iter()
569            .map(Self::parse_variant_attributes)
570            .collect::<Result<Vec<VariantAttributes>, Diagnostics>>()?
571            .into_iter()
572            .filter(|variant| variant.type_and_content.is_some())
573            .collect::<Vec<_>>();
574
575        let derive_value = DeriveToResponseValue::from_attributes(attributes)?;
576        if let Some(derive_value) = &derive_value {
577            if (!content.is_empty() && derive_value.example.is_some())
578                || (!content.is_empty() && derive_value.examples.is_some())
579            {
580                let ident = derive_value
581                    .example
582                    .as_ref()
583                    .map(|(_, ident)| ident)
584                    .or_else(|| derive_value.examples.as_ref().map(|(_, ident)| ident))
585                    .expect("Expected `example` or `examples` to be present");
586                return Err(
587                    Diagnostics::with_span(ident.span(),
588                        "Enum with `#[content]` attribute in variant cannot have enum level `example` or `examples` defined")
589                    .help(format!("Try defining `{}` on the enum variant", ident))
590                );
591            }
592        }
593
594        let generics = Generics::default();
595        let root = &Root {
596            ident,
597            attributes,
598            generics: &generics,
599        };
600        let inline_schema = EnumSchema::new(root, variants)?;
601
602        let response_value = if content.is_empty() {
603            if let Some(derive_value) = derive_value {
604                ResponseValue::from_derive_to_response_value(
605                    derive_value,
606                    Schema::Default(DefaultSchema::None),
607                    description,
608                )
609            } else {
610                ResponseValue::from_schema(
611                    Schema::Default(DefaultSchema::Raw {
612                        tokens: inline_schema.to_token_stream(),
613                        ty: Cow::Owned(ty),
614                    }),
615                    description,
616                )
617            }
618        } else {
619            let content = content
620                .into_iter()
621                .map(
622                    |VariantAttributes {
623                         type_and_content,
624                         derive_value,
625                         is_inline,
626                     }| {
627                        let (content_type, schema) = if let Some((ty, content)) = type_and_content {
628                            (
629                                Some(content.into()),
630                                Some(Schema::Default(DefaultSchema::TypePath(ParsedType {
631                                    ty: Cow::Borrowed(ty),
632                                    is_inline,
633                                }))),
634                            )
635                        } else {
636                            (None, None)
637                        };
638                        let (example, examples) = if let Some(derive_value) = derive_value {
639                            (
640                                derive_value.example.map(|(example, _)| example),
641                                derive_value.examples.map(|(examples, _)| examples),
642                            )
643                        } else {
644                            (None, None)
645                        };
646
647                        MediaTypeAttr {
648                            content_type,
649                            schema: schema.unwrap_or_else(|| Schema::Default(DefaultSchema::None)),
650                            example,
651                            examples: examples.unwrap_or_default(),
652                            ..MediaTypeAttr::default()
653                        }
654                    },
655                )
656                .collect::<Vec<_>>();
657
658            let mut response = if let Some(derive_value) = derive_value {
659                ResponseValue::from_derive_to_response_value(
660                    derive_value,
661                    Schema::Default(DefaultSchema::None),
662                    description,
663                )
664            } else {
665                ResponseValue::from_schema(
666                    Schema::Default(DefaultSchema::Raw {
667                        tokens: inline_schema.to_token_stream(),
668                        ty: Cow::Owned(ty),
669                    }),
670                    description,
671                )
672            };
673            response.content = content;
674
675            response
676        };
677
678        Ok(Self(response_value.into()))
679    }
680
681    fn parse_variant_attributes(variant: &Variant) -> Result<VariantAttributes, Diagnostics> {
682        let variant_derive_response_value =
683            DeriveToResponseValue::from_attributes(variant.attrs.as_slice())?;
684        // named enum variant should not have field attributes
685        if let Fields::Named(named_fields) = &variant.fields {
686            if let Some(diagnostics) = Self::validate_attributes(
687                named_fields.named.iter().flat_map(|field| &field.attrs),
688                Self::has_no_field_attributes,
689            )
690            .collect::<Option<Diagnostics>>()
691            {
692                return Err(diagnostics);
693            }
694        };
695
696        let field = variant.fields.iter().next();
697
698        let content_type = field.and_then_try(|field| {
699            field
700                .attrs
701                .iter()
702                .find(|attribute| attribute.path().get_ident().unwrap() == "content")
703                .map_try(|attribute| {
704                    attribute
705                        .parse_args_with(|input: ParseStream| input.parse::<LitStr>())
706                        .map(|content| content.value())
707                        .map_err(Diagnostics::from)
708                })
709        })?;
710
711        let is_inline = field
712            .map(|field| {
713                field
714                    .attrs
715                    .iter()
716                    .any(|attribute| attribute.path().get_ident().unwrap() == "to_schema")
717            })
718            .unwrap_or(false);
719
720        Ok(VariantAttributes {
721            type_and_content: field.map(|field| &field.ty).zip(content_type),
722            derive_value: variant_derive_response_value,
723            is_inline,
724        })
725    }
726}
727
728struct ToResponseUnitStructResponse<'u>(ResponseTuple<'u>);
729
730impl Response for ToResponseUnitStructResponse<'_> {}
731
732impl ToResponseUnitStructResponse<'_> {
733    fn new(attributes: &[Attribute]) -> Result<Self, Diagnostics> {
734        if let Some(diagnostics) =
735            Self::validate_attributes(attributes, Self::has_no_field_attributes)
736                .collect::<Option<Diagnostics>>()
737        {
738            return Err(diagnostics);
739        }
740
741        let derive_value = DeriveToResponseValue::from_attributes(attributes)?;
742        let description = {
743            let s = CommentAttributes::from_attributes(attributes).as_formatted_string();
744            parse_utils::LitStrOrExpr::LitStr(LitStr::new(&s, Span::call_site()))
745        };
746
747        let response_value = if let Some(derive_value) = derive_value {
748            ResponseValue::from_derive_to_response_value(
749                derive_value,
750                Schema::Default(DefaultSchema::None),
751                description,
752            )
753        } else {
754            ResponseValue {
755                description,
756                ..Default::default()
757            }
758        };
759
760        Ok(Self(response_value.into()))
761    }
762}