-
Notifications
You must be signed in to change notification settings - Fork 1
Expand file tree
/
Copy pathattribute.rs
More file actions
114 lines (95 loc) · 3.17 KB
/
Copy pathattribute.rs
File metadata and controls
114 lines (95 loc) · 3.17 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
use proc_macro2::TokenStream;
use quote::quote;
use syn::{Attribute, Error, Result, Type, spanned::Spanned};
use crate::status::Status;
pub struct HttpErrorAttribute {
pub status: Option<Status>,
pub base: Option<Type>,
pub axum: bool,
pub utoipa: bool,
}
impl<'a> HttpErrorAttribute {
pub fn parse_slice(input: &'a [Attribute]) -> Result<Option<Self>> {
let mut result = None;
for attribute in input {
if !attribute.meta.path().is_ident("http") {
continue;
}
if result.is_some() {
return Err(Error::new(
attribute.span(),
"only a single `http` attribute is allowed",
));
}
result = Some(Self::parse(attribute)?);
}
Ok(result)
}
pub fn parse(attribute: &'a Attribute) -> Result<Self> {
let mut status = None;
let mut base = None;
let mut axum = false;
let mut utoipa = false;
attribute.parse_nested_meta(|meta| {
if meta.path.is_ident("status") {
status = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("base") {
base = Some(meta.value()?.parse()?);
Ok(())
} else if meta.path.is_ident("axum") {
axum = true;
Ok(())
} else if meta.path.is_ident("utoipa") {
utoipa = true;
Ok(())
} else {
Err(meta.error("unknown parameter"))
}
})?;
Ok(Self {
status,
base,
axum,
utoipa,
})
}
pub fn status(&self) -> TokenStream {
if let Some(status) = &self.status {
let status = status.as_ident();
quote!(::breach::http::StatusCode::#status)
} else {
quote!(compile_error!("missing `#[http(status = ..)]` attribute"))
}
}
pub fn responses(&self, r#type: Option<TokenStream>) -> TokenStream {
if let Some(status) = &self.status {
let code = status.code.as_str();
let content = r#type.map(|r#type| {
// TODO: Attempt to infer content type from schema?
quote! {
.content(
"application/json",
::utoipa::openapi::content::ContentBuilder::new()
.schema(Some(<#r#type as ::utoipa::PartialSchema>::schema()))
.build()
)
}
});
quote! {
::std::collections::BTreeMap::from_iter([
(
#code.to_owned(),
::utoipa::openapi::RefOr::T(
::utoipa::openapi::response::ResponseBuilder::new()
#content
.build()
),
),
])
}
} else {
quote!(compile_error!("missing `#[http(status = ..)]` attribute"))
}
}
}