11use proc_macro2:: TokenStream ;
22use quote:: quote;
3- use syn:: { Attribute , Error , Result , Type , spanned:: Spanned } ;
3+ use syn:: { Attribute , Error , Expr , Result , Type , spanned:: Spanned } ;
44
55use crate :: status:: Status ;
66
77pub struct HttpErrorAttribute {
88 pub status : Option < Status > ,
9+ }
10+
11+ impl < ' a > HttpErrorAttribute {
12+ pub fn parse_slice ( input : & ' a [ Attribute ] ) -> Result < Option < Self > > {
13+ let mut result = None ;
14+
15+ for attribute in input {
16+ if !attribute. meta . path ( ) . is_ident ( "http" ) {
17+ continue ;
18+ }
19+
20+ if result. is_some ( ) {
21+ return Err ( Error :: new (
22+ attribute. span ( ) ,
23+ "only a single `http` attribute is allowed" ,
24+ ) ) ;
25+ }
26+
27+ result = Some ( Self :: parse ( attribute) ?) ;
28+ }
29+
30+ Ok ( result)
31+ }
32+
33+ pub fn parse ( attribute : & ' a Attribute ) -> Result < Self > {
34+ let mut status = None ;
35+
36+ attribute. parse_nested_meta ( |meta| {
37+ if meta. path . is_ident ( "status" ) {
38+ status = Some ( meta. value ( ) ?. parse ( ) ?) ;
39+
40+ Ok ( ( ) )
41+ } else {
42+ Err ( meta. error ( "unknown parameter" ) )
43+ }
44+ } ) ?;
45+
46+ Ok ( Self { status } )
47+ }
48+
49+ pub fn status ( & self ) -> TokenStream {
50+ status ( self . status . as_ref ( ) )
51+ }
52+
53+ pub fn responses ( & self , r#type : Option < TokenStream > ) -> TokenStream {
54+ responses ( self . status . as_ref ( ) , r#type)
55+ }
56+ }
57+
58+ pub struct HttpErrorDataAttribute {
59+ pub status : Option < Status > ,
960 pub base : Option < Type > ,
1061 pub axum : bool ,
62+ pub axum_hook : Option < Expr > ,
1163 pub utoipa : bool ,
1264}
1365
14- impl < ' a > HttpErrorAttribute {
66+ impl < ' a > HttpErrorDataAttribute {
1567 pub fn parse_slice ( input : & ' a [ Attribute ] ) -> Result < Option < Self > > {
1668 let mut result = None ;
1769
@@ -37,6 +89,7 @@ impl<'a> HttpErrorAttribute {
3789 let mut status = None ;
3890 let mut base = None ;
3991 let mut axum = false ;
92+ let mut axum_hook = None ;
4093 let mut utoipa = false ;
4194
4295 attribute. parse_nested_meta ( |meta| {
@@ -51,6 +104,10 @@ impl<'a> HttpErrorAttribute {
51104 } else if meta. path . is_ident ( "axum" ) {
52105 axum = true ;
53106
107+ Ok ( ( ) )
108+ } else if meta. path . is_ident ( "axum_hook" ) {
109+ axum_hook = Some ( meta. value ( ) ?. parse ( ) ?) ;
110+
54111 Ok ( ( ) )
55112 } else if meta. path . is_ident ( "utoipa" ) {
56113 utoipa = true ;
@@ -65,50 +122,59 @@ impl<'a> HttpErrorAttribute {
65122 status,
66123 base,
67124 axum,
125+ axum_hook,
68126 utoipa,
69127 } )
70128 }
71129
72130 pub fn status ( & self ) -> TokenStream {
73- if let Some ( status) = & self . status {
74- let status = status. as_ident ( ) ;
75-
76- quote ! ( :: breach:: http:: StatusCode :: #status)
77- } else {
78- quote ! ( compile_error!( "missing `#[http(status = ..)]` attribute" ) )
79- }
131+ status ( self . status . as_ref ( ) )
80132 }
81133
82134 pub fn responses ( & self , r#type : Option < TokenStream > ) -> TokenStream {
83- if let Some ( status) = & self . status {
84- let code = status. code . as_str ( ) ;
85-
86- let content = r#type. map ( |r#type| {
87- // TODO: Attempt to infer content type from schema?
88- quote ! {
89- . content(
90- "application/json" ,
91- :: utoipa:: openapi:: content:: ContentBuilder :: new( )
92- . schema( Some ( <#r#type as :: utoipa:: PartialSchema >:: schema( ) ) )
93- . build( )
94- )
95- }
96- } ) ;
135+ responses ( self . status . as_ref ( ) , r#type)
136+ }
137+ }
138+
139+ fn status ( status : Option < & Status > ) -> TokenStream {
140+ if let Some ( status) = status {
141+ let status = status. as_ident ( ) ;
97142
143+ quote ! ( :: breach:: http:: StatusCode :: #status)
144+ } else {
145+ quote ! ( compile_error!( "missing `#[http(status = ..)]` attribute" ) )
146+ }
147+ }
148+
149+ fn responses ( status : Option < & Status > , r#type : Option < TokenStream > ) -> TokenStream {
150+ if let Some ( status) = status {
151+ let code = status. code . as_str ( ) ;
152+
153+ let content = r#type. map ( |r#type| {
154+ // TODO: Attempt to infer content type from schema?
98155 quote ! {
99- :: std:: collections:: BTreeMap :: from_iter( [
100- (
101- #code. to_owned( ) ,
102- :: utoipa:: openapi:: RefOr :: T (
103- :: utoipa:: openapi:: response:: ResponseBuilder :: new( )
104- #content
105- . build( )
106- ) ,
107- ) ,
108- ] )
156+ . content(
157+ "application/json" ,
158+ :: utoipa:: openapi:: content:: ContentBuilder :: new( )
159+ . schema( Some ( <#r#type as :: utoipa:: PartialSchema >:: schema( ) ) )
160+ . build( )
161+ )
109162 }
110- } else {
111- quote ! ( compile_error!( "missing `#[http(status = ..)]` attribute" ) )
163+ } ) ;
164+
165+ quote ! {
166+ :: std:: collections:: BTreeMap :: from_iter( [
167+ (
168+ #code. to_owned( ) ,
169+ :: utoipa:: openapi:: RefOr :: T (
170+ :: utoipa:: openapi:: response:: ResponseBuilder :: new( )
171+ #content
172+ . build( )
173+ ) ,
174+ ) ,
175+ ] )
112176 }
177+ } else {
178+ quote ! ( compile_error!( "missing `#[http(status = ..)]` attribute" ) )
113179 }
114180}
0 commit comments