@@ -2,21 +2,50 @@ use crate::utils::{
22 add_extra_generic_param, numbered_vars, replace_self:: DeriveInputExt as _,
33 AttrParams , DeriveType , MultiFieldData , State ,
44} ;
5- use proc_macro2:: TokenStream ;
5+ use proc_macro2:: { Span , TokenStream } ;
66use quote:: { quote, ToTokens } ;
7- use syn:: { DeriveInput , Result } ;
7+ use syn:: { Attribute , DeriveInput , Ident , Result } ;
88
9- use crate :: utils:: HashMap ;
9+ use crate :: utils:: {
10+ attr:: { self , ParseMultiple as _} ,
11+ HashMap , Spanning ,
12+ } ;
1013
1114/// Provides the hook to expand `#[derive(TryInto)]` into an implementation of `TryInto`
1215#[ allow( clippy:: cognitive_complexity) ]
1316pub fn expand ( input : & DeriveInput , trait_name : & ' static str ) -> Result < TokenStream > {
14- let input = & input. replace_self_type ( ) ;
17+ let input = & mut input. replace_self_type ( ) ;
18+
19+ let trait_attr = "try_into" ;
20+
21+ let error_attrs: Vec < ( usize , Attribute ) > = input
22+ . attrs
23+ . iter ( )
24+ . enumerate ( )
25+ . filter ( |( _, attr) | attr. path ( ) . is_ident ( trait_attr) )
26+ . filter ( |( _, attr) | attr. parse_args_with ( detect_error_attr) . is_ok ( ) )
27+ . map ( |( i, attr) | ( i, attr. clone ( ) ) )
28+ . collect ( ) ;
29+
30+ for ( i, _) in & error_attrs {
31+ let _ = & mut input. attrs . remove ( * i) ;
32+ }
33+
34+ let error_attrs = error_attrs
35+ . into_iter ( )
36+ . map ( |( _, attr) | attr)
37+ . collect :: < Vec < Attribute > > ( ) ;
38+
39+ let custom_error = attr:: Error :: parse_attrs (
40+ error_attrs,
41+ & Ident :: new ( trait_attr, Span :: call_site ( ) ) ,
42+ ) ?
43+ . map ( Spanning :: into_inner) ;
1544
1645 let state = State :: with_attr_params (
1746 input,
1847 trait_name,
19- "try_into" . into ( ) ,
48+ trait_attr . into ( ) ,
2049 AttrParams {
2150 enum_ : vec ! [ "ignore" , "owned" , "ref" , "ref_mut" ] ,
2251 variant : vec ! [ "ignore" , "owned" , "ref" , "ref_mut" ] ,
@@ -101,26 +130,36 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
101130 input. generics . split_for_impl ( )
102131 } ;
103132
104- let error = quote ! {
133+ let mut error_ty = quote ! {
105134 derive_more:: TryIntoError <#reference_with_lifetime #input_type #ty_generics>
106135 } ;
107136
137+ let mut error_conv = quote ! { } ;
138+
139+ if let Some ( custom_error) = custom_error. as_ref ( ) {
140+ error_ty = custom_error. ty . to_token_stream ( ) ;
141+ error_conv = custom_error. conv . as_ref ( ) . map_or_else (
142+ || quote ! { . map_err( derive_more:: core:: convert:: Into :: into) } ,
143+ |conv| quote ! { . map_err( #conv) } ,
144+ ) ;
145+ }
146+
108147 let try_from = quote ! {
109148 #[ automatically_derived]
110149 impl #impl_generics derive_more:: core:: convert:: TryFrom <
111150 #reference_with_lifetime #input_type #ty_generics
112151 > for ( #( #reference_with_lifetime #original_types) , * ) #where_clause {
113- type Error = #error ;
152+ type Error = #error_ty ;
114153
115154 #[ inline]
116155 fn try_from(
117156 value: #reference_with_lifetime #input_type #ty_generics,
118- ) -> derive_more:: core:: result:: Result <Self , #error > {
157+ ) -> derive_more:: core:: result:: Result <Self , #error_ty > {
119158 match value {
120159 #( #matchers) |* => derive_more:: core:: result:: Result :: Ok ( #vars) ,
121160 _ => derive_more:: core:: result:: Result :: Err (
122161 derive_more:: TryIntoError :: new( value, #variant_names, #output_type) ,
123- ) ,
162+ ) #error_conv ,
124163 }
125164 }
126165 }
@@ -129,3 +168,17 @@ pub fn expand(input: &DeriveInput, trait_name: &'static str) -> Result<TokenStre
129168 }
130169 Ok ( tokens)
131170}
171+
172+ fn detect_error_attr ( input : syn:: parse:: ParseStream ) -> Result < ( ) > {
173+ mod ident {
174+ syn:: custom_keyword!( error) ;
175+ }
176+
177+ let ahead = input. lookahead1 ( ) ;
178+ if ahead. peek ( ident:: error) {
179+ let _ = input. parse :: < TokenStream > ( ) ;
180+ Ok ( ( ) )
181+ } else {
182+ Err ( ahead. error ( ) )
183+ }
184+ }
0 commit comments