use crate::utoipa::config::ResponseConfig;
use syn::parse::{Parse, ParseStream};
use syn::*;
#[derive(Debug, Clone)]
pub struct UResponseAttr {
pub status: u16,
pub description: String,
pub body: Option<Type>,
pub content_type: Option<String>,
}
impl Parse for UResponseAttr {
fn parse(input: ParseStream) -> Result<Self> {
let mut status = 200;
let mut description = String::new();
let mut body = None;
let mut content_type = None;
while !input.is_empty() {
let key: Ident = input.parse()?;
input.parse::<Token![=]>()?;
match key.to_string().as_str() {
"status" => {
let lit: LitInt = input.parse()?;
status = lit.base10_parse()?;
}
"description" => {
let lit: LitStr = input.parse()?;
description = lit.value();
}
"body" => {
body = Some(input.parse()?);
}
"content_type" => {
let lit: LitStr = input.parse()?;
content_type = Some(lit.value());
}
_ => {
return Err(Error::new(key.span(), format!("Unknown key: {}", key)));
}
}
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
}
Ok(UResponseAttr {
status,
description,
body,
content_type,
})
}
}
impl From<UResponseAttr> for ResponseConfig {
fn from(attr: UResponseAttr) -> Self {
ResponseConfig {
status: attr.status,
description: attr.description,
body: attr.body,
content_type: attr.content_type,
}
}
}
#[derive(Debug, Clone)]
pub struct UTagAttr {
pub tag: String,
}
impl Parse for UTagAttr {
fn parse(input: ParseStream) -> Result<Self> {
let lit: LitStr = input.parse()?;
Ok(UTagAttr { tag: lit.value() })
}
}
#[derive(Debug, Clone)]
pub struct USummaryAttr {
pub summary: String,
}
impl Parse for USummaryAttr {
fn parse(input: ParseStream) -> Result<Self> {
let lit: LitStr = input.parse()?;
Ok(USummaryAttr {
summary: lit.value(),
})
}
}
#[derive(Debug, Clone)]
pub struct UDescriptionAttr {
pub description: String,
}
impl Parse for UDescriptionAttr {
fn parse(input: ParseStream) -> Result<Self> {
let lit: LitStr = input.parse()?;
Ok(UDescriptionAttr {
description: lit.value(),
})
}
}
#[derive(Debug, Clone)]
pub struct URequestBodyAttr {
pub content: Type,
pub description: Option<String>,
pub content_type: Option<String>,
}
impl Parse for URequestBodyAttr {
fn parse(input: ParseStream) -> Result<Self> {
let mut content = None;
let mut description = None;
let mut content_type = None;
while !input.is_empty() {
let key: Ident = input.parse()?;
input.parse::<Token![=]>()?;
match key.to_string().as_str() {
"content" => {
content = Some(input.parse()?);
}
"description" => {
let lit: LitStr = input.parse()?;
description = Some(lit.value());
}
"content_type" => {
let lit: LitStr = input.parse()?;
content_type = Some(lit.value());
}
_ => {
return Err(Error::new(key.span(), format!("Unknown key: {}", key)));
}
}
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
}
let content =
content.ok_or_else(|| Error::new(input.span(), "Missing required field: content"))?;
Ok(URequestBodyAttr {
content,
description,
content_type,
})
}
}
#[derive(Debug, Clone)]
#[allow(dead_code)]
pub struct UParamAttr {
pub name: String,
pub description: Option<String>,
pub example: Option<Expr>,
pub deprecated: bool,
}
impl Parse for UParamAttr {
fn parse(input: ParseStream) -> Result<Self> {
let mut name = String::new();
let mut description = None;
let mut example = None;
let mut deprecated = false;
while !input.is_empty() {
let key: Ident = input.parse()?;
input.parse::<Token![=]>()?;
match key.to_string().as_str() {
"name" => {
let lit: LitStr = input.parse()?;
name = lit.value();
}
"description" => {
let lit: LitStr = input.parse()?;
description = Some(lit.value());
}
"example" => {
example = Some(input.parse()?);
}
"deprecated" => {
let lit: LitBool = input.parse()?;
deprecated = lit.value;
}
_ => {
return Err(Error::new(key.span(), format!("Unknown key: {}", key)));
}
}
if input.peek(Token![,]) {
input.parse::<Token![,]>()?;
}
}
Ok(UParamAttr {
name,
description,
example,
deprecated,
})
}
}
pub fn parse_utoipa_attrs(attrs: &[Attribute]) -> crate::utoipa::config::OpenApiConfig {
let mut config = crate::utoipa::config::OpenApiConfig::new();
for attr in attrs {
let path = attr.path();
if path.is_ident("u_response") {
if let Ok(resp) = attr.parse_args::<UResponseAttr>() {
config.user_responses.push(resp.into());
}
} else if path.is_ident("u_tag") {
if let Ok(tag) = attr.parse_args::<UTagAttr>() {
config.user_tags.push(tag.tag);
}
} else if path.is_ident("u_summary") {
if let Ok(summary) = attr.parse_args::<USummaryAttr>() {
config.user_summary = Some(summary.summary);
}
} else if path.is_ident("u_description") {
if let Ok(desc) = attr.parse_args::<UDescriptionAttr>() {
config.user_description = Some(desc.description);
}
} else if path.is_ident("u_deprecated") {
config.deprecated = true;
} else if path.is_ident("u_request_body")
&& let Ok(req_body) = attr.parse_args::<URequestBodyAttr>()
{
config.user_request_body = Some(crate::utoipa::config::RequestBodyConfig {
ty: req_body.content,
description: req_body.description,
required: true,
content_type: req_body
.content_type
.unwrap_or_else(|| "application/json".to_string()),
});
}
}
config
}