66
66
extern crate proc_macro;
67
67
68
68
use proc_macro:: TokenStream ;
69
- use proc_macro2:: Span ;
69
+ use proc_macro2:: { Span , TokenStream as TokenStream2 } ;
70
70
use quote:: quote;
71
71
use syn:: { Data , Fields , Ident } ;
72
72
@@ -87,11 +87,7 @@ use syn::{Data, Fields, Ident};
87
87
//
88
88
// Solution: use the dummy const trick. For some reason, `extern crate` statements are allowed
89
89
// here, but everything from the surrounding module is in scope. This trick is taken from serde.
90
- fn dummy_const_trick < T : quote:: ToTokens > (
91
- trait_ : & str ,
92
- name : & proc_macro2:: Ident ,
93
- exp : T ,
94
- ) -> proc_macro2:: TokenStream {
90
+ fn dummy_const_trick ( trait_ : & str , name : & Ident , exp : TokenStream2 ) -> TokenStream2 {
95
91
let dummy_const = Ident :: new (
96
92
& format ! ( "_IMPL_NUM_{}_FOR_{}" , trait_, unraw( name) ) ,
97
93
Span :: call_site ( ) ,
@@ -107,7 +103,7 @@ fn dummy_const_trick<T: quote::ToTokens>(
107
103
}
108
104
}
109
105
110
- fn unraw ( ident : & proc_macro2 :: Ident ) -> String {
106
+ fn unraw ( ident : & Ident ) -> String {
111
107
ident. to_string ( ) . trim_start_matches ( "r#" ) . to_owned ( )
112
108
}
113
109
@@ -137,26 +133,51 @@ fn newtype_inner(data: &syn::Data) -> Option<syn::Type> {
137
133
}
138
134
}
139
135
140
- // If there as `num_traits` MetaNameValue attribute within the slice,
141
- // retrieve its value, and use it to create an `Ident` to be used to import
142
- // the `num_traits` crate.
143
- fn find_explicit_import_ident ( attrs : & [ syn:: Attribute ] ) -> Option < syn:: Ident > {
144
- for attr in attrs {
145
- if let Ok ( syn:: Meta :: NameValue ( mnv) ) = attr. parse_meta ( ) {
146
- if mnv. path . is_ident ( "num_traits" ) {
147
- match mnv. lit {
148
- syn:: Lit :: Str ( lit_str) => {
149
- let import_str = & lit_str. value ( ) ;
150
- let span = proc_macro2:: Span :: call_site ( ) ;
151
- let import_ident = syn:: Ident :: new ( import_str, span) ;
152
- return Some ( import_ident) ;
136
+ struct NumTraits {
137
+ import : Ident ,
138
+ explicit : bool ,
139
+ }
140
+
141
+ impl quote:: ToTokens for NumTraits {
142
+ fn to_tokens ( & self , tokens : & mut TokenStream2 ) {
143
+ self . import . to_tokens ( tokens) ;
144
+ }
145
+ }
146
+
147
+ impl NumTraits {
148
+ fn new ( ast : & syn:: DeriveInput ) -> Self {
149
+ // If there is a `num_traits` MetaNameValue attribute on the input,
150
+ // retrieve its value, and use it to create an `Ident` to be used
151
+ // to import the `num_traits` crate.
152
+ for attr in & ast. attrs {
153
+ if let Ok ( syn:: Meta :: NameValue ( mnv) ) = attr. parse_meta ( ) {
154
+ if mnv. path . is_ident ( "num_traits" ) {
155
+ if let syn:: Lit :: Str ( lit_str) = mnv. lit {
156
+ return NumTraits {
157
+ import : syn:: Ident :: new ( & lit_str. value ( ) , lit_str. span ( ) ) ,
158
+ explicit : true ,
159
+ } ;
160
+ } else {
161
+ panic ! ( "#[num_traits] attribute value must be a str" ) ;
153
162
}
154
- _ => panic ! ( "#[num_traits] attribute value must be a str" ) ,
155
163
}
156
164
}
157
165
}
166
+
167
+ // Otherwise, we'll implicitly import our own.
168
+ NumTraits {
169
+ import : Ident :: new ( "_num_traits" , Span :: call_site ( ) ) ,
170
+ explicit : false ,
171
+ }
172
+ }
173
+
174
+ fn wrap ( & self , trait_ : & str , name : & Ident , output : TokenStream2 ) -> TokenStream2 {
175
+ if self . explicit {
176
+ output
177
+ } else {
178
+ dummy_const_trick ( trait_, & name, output)
179
+ }
158
180
}
159
- None
160
181
}
161
182
162
183
/// Derives [`num_traits::FromPrimitive`][from] for simple enums and newtypes.
@@ -212,13 +233,7 @@ pub fn from_primitive(input: TokenStream) -> TokenStream {
212
233
let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
213
234
let name = & ast. ident ;
214
235
215
- let explicit_import = find_explicit_import_ident ( & ast. attrs ) ;
216
- let is_explicit_import = explicit_import. is_some ( ) ;
217
-
218
- let import = match explicit_import {
219
- Some ( ident) => ident,
220
- None => syn:: Ident :: new ( "_num_traits" , proc_macro2:: Span :: call_site ( ) ) ,
221
- } ;
236
+ let import = NumTraits :: new ( & ast) ;
222
237
223
238
let impl_ = if let Some ( inner_ty) = newtype_inner ( & ast. data ) {
224
239
quote ! {
@@ -320,11 +335,7 @@ pub fn from_primitive(input: TokenStream) -> TokenStream {
320
335
}
321
336
} ;
322
337
323
- if is_explicit_import {
324
- impl_. into ( )
325
- } else {
326
- dummy_const_trick ( "FromPrimitive" , & name, impl_) . into ( )
327
- }
338
+ import. wrap ( "FromPrimitive" , & name, impl_) . into ( )
328
339
}
329
340
330
341
/// Derives [`num_traits::ToPrimitive`][to] for simple enums and newtypes.
@@ -380,13 +391,7 @@ pub fn to_primitive(input: TokenStream) -> TokenStream {
380
391
let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
381
392
let name = & ast. ident ;
382
393
383
- let explicit_import = find_explicit_import_ident ( & ast. attrs ) ;
384
- let is_explicit_import = explicit_import. is_some ( ) ;
385
-
386
- let import = match explicit_import {
387
- Some ( ident) => ident,
388
- None => syn:: Ident :: new ( "_num_traits" , proc_macro2:: Span :: call_site ( ) ) ,
389
- } ;
394
+ let import = NumTraits :: new ( & ast) ;
390
395
391
396
let impl_ = if let Some ( inner_ty) = newtype_inner ( & ast. data ) {
392
397
quote ! {
@@ -489,11 +494,7 @@ pub fn to_primitive(input: TokenStream) -> TokenStream {
489
494
}
490
495
} ;
491
496
492
- if is_explicit_import {
493
- impl_. into ( )
494
- } else {
495
- dummy_const_trick ( "ToPrimitive" , & name, impl_) . into ( )
496
- }
497
+ import. wrap ( "ToPrimitive" , & name, impl_) . into ( )
497
498
}
498
499
499
500
const NEWTYPE_ONLY : & str = "This trait can only be derived for newtypes" ;
@@ -560,13 +561,7 @@ pub fn num_cast(input: TokenStream) -> TokenStream {
560
561
let name = & ast. ident ;
561
562
let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
562
563
563
- let explicit_import = find_explicit_import_ident ( & ast. attrs ) ;
564
- let is_explicit_import = explicit_import. is_some ( ) ;
565
-
566
- let import = match explicit_import {
567
- Some ( ident) => ident,
568
- None => syn:: Ident :: new ( "_num_traits" , proc_macro2:: Span :: call_site ( ) ) ,
569
- } ;
564
+ let import = NumTraits :: new ( & ast) ;
570
565
571
566
let impl_ = quote ! {
572
567
impl #import:: NumCast for #name {
@@ -576,11 +571,7 @@ pub fn num_cast(input: TokenStream) -> TokenStream {
576
571
}
577
572
} ;
578
573
579
- if is_explicit_import {
580
- impl_. into ( )
581
- } else {
582
- dummy_const_trick ( "NumCast" , & name, impl_) . into ( )
583
- }
574
+ import. wrap ( "NumCast" , & name, impl_) . into ( )
584
575
}
585
576
586
577
/// Derives [`num_traits::Zero`][zero] for newtypes. The inner type must already implement `Zero`.
@@ -592,13 +583,7 @@ pub fn zero(input: TokenStream) -> TokenStream {
592
583
let name = & ast. ident ;
593
584
let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
594
585
595
- let explicit_import = find_explicit_import_ident ( & ast. attrs ) ;
596
- let is_explicit_import = explicit_import. is_some ( ) ;
597
-
598
- let import = match explicit_import {
599
- Some ( ident) => ident,
600
- None => syn:: Ident :: new ( "_num_traits" , proc_macro2:: Span :: call_site ( ) ) ,
601
- } ;
586
+ let import = NumTraits :: new ( & ast) ;
602
587
603
588
let impl_ = quote ! {
604
589
impl #import:: Zero for #name {
@@ -611,11 +596,7 @@ pub fn zero(input: TokenStream) -> TokenStream {
611
596
}
612
597
} ;
613
598
614
- if is_explicit_import {
615
- impl_. into ( )
616
- } else {
617
- dummy_const_trick ( "Zero" , & name, impl_) . into ( )
618
- }
599
+ import. wrap ( "Zero" , & name, impl_) . into ( )
619
600
}
620
601
621
602
/// Derives [`num_traits::One`][one] for newtypes. The inner type must already implement `One`.
@@ -627,13 +608,7 @@ pub fn one(input: TokenStream) -> TokenStream {
627
608
let name = & ast. ident ;
628
609
let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
629
610
630
- let explicit_import = find_explicit_import_ident ( & ast. attrs ) ;
631
- let is_explicit_import = explicit_import. is_some ( ) ;
632
-
633
- let import = match explicit_import {
634
- Some ( ident) => ident,
635
- None => syn:: Ident :: new ( "_num_traits" , proc_macro2:: Span :: call_site ( ) ) ,
636
- } ;
611
+ let import = NumTraits :: new ( & ast) ;
637
612
638
613
let impl_ = quote ! {
639
614
impl #import:: One for #name {
@@ -646,11 +621,7 @@ pub fn one(input: TokenStream) -> TokenStream {
646
621
}
647
622
} ;
648
623
649
- if is_explicit_import {
650
- impl_. into ( )
651
- } else {
652
- dummy_const_trick ( "One" , & name, impl_) . into ( )
653
- }
624
+ import. wrap ( "One" , & name, impl_) . into ( )
654
625
}
655
626
656
627
/// Derives [`num_traits::Num`][num] for newtypes. The inner type must already implement `Num`.
@@ -662,13 +633,7 @@ pub fn num(input: TokenStream) -> TokenStream {
662
633
let name = & ast. ident ;
663
634
let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
664
635
665
- let explicit_import = find_explicit_import_ident ( & ast. attrs ) ;
666
- let is_explicit_import = explicit_import. is_some ( ) ;
667
-
668
- let import = match explicit_import {
669
- Some ( ident) => ident,
670
- None => syn:: Ident :: new ( "_num_traits" , proc_macro2:: Span :: call_site ( ) ) ,
671
- } ;
636
+ let import = NumTraits :: new ( & ast) ;
672
637
673
638
let impl_ = quote ! {
674
639
impl #import:: Num for #name {
@@ -679,11 +644,7 @@ pub fn num(input: TokenStream) -> TokenStream {
679
644
}
680
645
} ;
681
646
682
- if is_explicit_import {
683
- impl_. into ( )
684
- } else {
685
- dummy_const_trick ( "Num" , & name, impl_) . into ( )
686
- }
647
+ import. wrap ( "Num" , & name, impl_) . into ( )
687
648
}
688
649
689
650
/// Derives [`num_traits::Float`][float] for newtypes. The inner type must already implement
@@ -696,13 +657,7 @@ pub fn float(input: TokenStream) -> TokenStream {
696
657
let name = & ast. ident ;
697
658
let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
698
659
699
- let explicit_import = find_explicit_import_ident ( & ast. attrs ) ;
700
- let is_explicit_import = explicit_import. is_some ( ) ;
701
-
702
- let import = match explicit_import {
703
- Some ( ident) => ident,
704
- None => syn:: Ident :: new ( "_num_traits" , proc_macro2:: Span :: call_site ( ) ) ,
705
- } ;
660
+ let import = NumTraits :: new ( & ast) ;
706
661
707
662
let impl_ = quote ! {
708
663
impl #import:: Float for #name {
@@ -881,9 +836,5 @@ pub fn float(input: TokenStream) -> TokenStream {
881
836
}
882
837
} ;
883
838
884
- if is_explicit_import {
885
- impl_. into ( )
886
- } else {
887
- dummy_const_trick ( "Float" , & name, impl_) . into ( )
888
- }
839
+ import. wrap ( "Float" , & name, impl_) . into ( )
889
840
}
0 commit comments