utoipa_gen/path/response/
header.rs1use proc_macro2::TokenStream;
2use quote::{quote, ToTokens};
3use syn::parse::{Parse, ParseStream};
4use syn::{Error, Generics, Ident, LitStr, Token};
5
6use crate::component::features::attributes::Inline;
7use crate::component::{ComponentSchema, Container, TypeTree};
8use crate::path::media_type::ParsedType;
9use crate::{parse_utils, Diagnostics, ToTokensDiagnostics};
10
11#[derive(Default)]
66#[cfg_attr(feature = "debug", derive(Debug))]
67pub struct Header {
68 pub name: String,
69 value_type: Option<ParsedType<'static>>,
70 description: Option<String>,
71}
72
73impl Parse for Header {
74 fn parse(input: syn::parse::ParseStream) -> syn::Result<Self> {
75 let mut header = Header {
76 name: input.parse::<LitStr>()?.value(),
77 ..Default::default()
78 };
79
80 if input.peek(Token![=]) {
81 input.parse::<Token![=]>()?;
82
83 header.value_type = Some(input.parse().map_err(|error| {
84 Error::new(
85 error.span(),
86 format!("unexpected token, expected type such as String, {error}"),
87 )
88 })?);
89 }
90
91 if !input.is_empty() {
92 input.parse::<Token![,]>()?;
93 }
94
95 if input.peek(syn::Ident) {
96 input
97 .parse::<Ident>()
98 .map_err(|error| {
99 Error::new(
100 error.span(),
101 format!("unexpected attribute, expected: description, {error}"),
102 )
103 })
104 .and_then(|ident| {
105 if ident != "description" {
106 return Err(Error::new(
107 ident.span(),
108 "unexpected attribute, expected: description",
109 ));
110 }
111 Ok(ident)
112 })?;
113 input.parse::<Token![=]>()?;
114 header.description = Some(input.parse::<LitStr>()?.value());
115 }
116
117 Ok(header)
118 }
119}
120
121impl ToTokensDiagnostics for Header {
122 fn to_tokens(&self, tokens: &mut TokenStream) -> Result<(), Diagnostics> {
123 if let Some(header_type) = &self.value_type {
124 let type_tree = TypeTree::from_type(header_type.ty.as_ref())?;
126
127 let media_type_schema = ComponentSchema::new(crate::component::ComponentSchemaProps {
128 type_tree: &type_tree,
129 features: vec![Inline::from(header_type.is_inline).into()],
130 description: None,
131 container: &Container {
132 generics: &Generics::default(),
133 },
134 })?
135 .to_token_stream();
136
137 tokens.extend(quote! {
138 utoipa::openapi::HeaderBuilder::new().schema(#media_type_schema)
139 })
140 } else {
141 tokens.extend(quote! {
143 Into::<utoipa::openapi::HeaderBuilder>::into(utoipa::openapi::Header::default())
144 })
145 };
146
147 if let Some(ref description) = self.description {
148 tokens.extend(quote! {
149 .description(Some(#description))
150 })
151 }
152
153 tokens.extend(quote! { .build() });
154
155 Ok(())
156 }
157}
158
159#[inline]
160pub fn headers(input: ParseStream) -> syn::Result<Vec<Header>> {
161 let headers;
162 syn::parenthesized!(headers in input);
163
164 parse_utils::parse_groups_collect(&headers)
165}