44#![ deny( missing_docs) ]
55extern crate proc_macro;
66
7+ use std:: collections:: HashMap ;
8+
79use mediatype:: MediaTypeBuf ;
810use proc_macro:: TokenStream ;
911use quote:: quote;
@@ -66,7 +68,12 @@ pub fn derive_accept_extractor(input: TokenStream) -> TokenStream {
6668 } )
6769 } ) ;
6870
71+ // Match arms with ty, subty and suffix
6972 let mut match_arms = Vec :: new ( ) ;
73+ // Match arms with ty only (for checking mediatypes like text/*)
74+ let mut match_arms_tys = HashMap :: new ( ) ;
75+ // Store first variant to fall back to if we don't have a default.
76+ let mut first_variant_name = None ;
7077
7178 for variant in & data. variants {
7279 let variant_name = & variant. ident ;
@@ -79,6 +86,16 @@ pub fn derive_accept_extractor(input: TokenStream) -> TokenStream {
7986 mediatype. suffix ( ) . map ( |s| s. as_str ( ) ) ,
8087 ) ;
8188
89+ if ty == "*" || subty == "*" {
90+ panic ! ( "Please use a concrete mediatype" ) ;
91+ }
92+
93+ if first_variant_name. is_none ( ) {
94+ first_variant_name = Some ( variant_name. clone ( ) ) ;
95+ }
96+
97+ match_arms_tys. insert ( ty. to_string ( ) , variant_name) ;
98+
8299 match & variant. fields {
83100 Fields :: Unit => {
84101 // quote encodes None to empty string, so we need to take extra
@@ -107,6 +124,22 @@ pub fn derive_accept_extractor(input: TokenStream) -> TokenStream {
107124 None
108125 } ;
109126
127+ let handle_star_star = if has_default {
128+ quote ! {
129+ return Ok ( #name:: default ( ) ) ;
130+ }
131+ } else {
132+ quote ! {
133+ return Ok ( #name:: #first_variant_name) ;
134+ }
135+ } ;
136+
137+ let match_arms_tys = match_arms_tys. iter ( ) . map ( |( ty, variant_name) | {
138+ quote ! {
139+ ( #ty) => return Ok ( #name:: #variant_name) ,
140+ }
141+ } ) ;
142+
110143 let expanded = quote ! {
111144 impl #impl_generics axum:: extract:: FromRequestParts <S > for #name #ty_generics #where_clause {
112145 type Rejection = axum_accept:: AcceptRejection ;
@@ -115,9 +148,23 @@ pub fn derive_accept_extractor(input: TokenStream) -> TokenStream {
115148 let mediatypes = axum_accept:: parse_mediatypes( & parts. headers) ?;
116149 #check_and_return_default
117150 for mt in mediatypes {
118- match ( mt. ty. as_str( ) , mt. subty. as_str( ) , mt. suffix. map( |s| s. as_str( ) ) ) {
119- #( #match_arms) *
120- _ => { } // continue searching
151+ match ( mt. ty. as_str( ) , mt. subty. as_str( ) ) {
152+ ( "*" , "*" ) => {
153+ // return either the the default or the first
154+ // variant
155+ #handle_star_star
156+ } ,
157+ // do we have any mediatype that shares the main type?
158+ // e.g. we offer text/plain and get accept: text/*
159+ ( _, "*" ) => match ( mt. ty. as_str( ) ) {
160+ #( #match_arms_tys) *
161+ _ => { } // continue searching
162+ } ,
163+ // do proper matching
164+ _ => match ( mt. ty. as_str( ) , mt. subty. as_str( ) , mt. suffix. map( |s| s. as_str( ) ) ) {
165+ #( #match_arms) *
166+ _ => { } // continue searching
167+ } ,
121168 }
122169 }
123170
0 commit comments