6666extern crate proc_macro;
6767
6868use proc_macro:: TokenStream ;
69- use proc_macro2:: Span ;
69+ use proc_macro2:: { Span , TokenStream as TokenStream2 } ;
7070use quote:: quote;
7171use syn:: { Data , Fields , Ident } ;
7272
@@ -87,11 +87,7 @@ use syn::{Data, Fields, Ident};
8787//
8888// Solution: use the dummy const trick. For some reason, `extern crate` statements are allowed
8989// 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 {
9591 let dummy_const = Ident :: new (
9692 & format ! ( "_IMPL_NUM_{}_FOR_{}" , trait_, unraw( name) ) ,
9793 Span :: call_site ( ) ,
@@ -107,7 +103,7 @@ fn dummy_const_trick<T: quote::ToTokens>(
107103 }
108104}
109105
110- fn unraw ( ident : & proc_macro2 :: Ident ) -> String {
106+ fn unraw ( ident : & Ident ) -> String {
111107 ident. to_string ( ) . trim_start_matches ( "r#" ) . to_owned ( )
112108}
113109
@@ -137,26 +133,51 @@ fn newtype_inner(data: &syn::Data) -> Option<syn::Type> {
137133 }
138134}
139135
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" ) ;
153162 }
154- _ => panic ! ( "#[num_traits] attribute value must be a str" ) ,
155163 }
156164 }
157165 }
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+ }
158180 }
159- None
160181}
161182
162183/// Derives [`num_traits::FromPrimitive`][from] for simple enums and newtypes.
@@ -212,13 +233,7 @@ pub fn from_primitive(input: TokenStream) -> TokenStream {
212233 let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
213234 let name = & ast. ident ;
214235
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) ;
222237
223238 let impl_ = if let Some ( inner_ty) = newtype_inner ( & ast. data ) {
224239 quote ! {
@@ -320,11 +335,7 @@ pub fn from_primitive(input: TokenStream) -> TokenStream {
320335 }
321336 } ;
322337
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 ( )
328339}
329340
330341/// Derives [`num_traits::ToPrimitive`][to] for simple enums and newtypes.
@@ -380,13 +391,7 @@ pub fn to_primitive(input: TokenStream) -> TokenStream {
380391 let ast: syn:: DeriveInput = syn:: parse ( input) . unwrap ( ) ;
381392 let name = & ast. ident ;
382393
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) ;
390395
391396 let impl_ = if let Some ( inner_ty) = newtype_inner ( & ast. data ) {
392397 quote ! {
@@ -489,11 +494,7 @@ pub fn to_primitive(input: TokenStream) -> TokenStream {
489494 }
490495 } ;
491496
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 ( )
497498}
498499
499500const NEWTYPE_ONLY : & str = "This trait can only be derived for newtypes" ;
@@ -560,13 +561,7 @@ pub fn num_cast(input: TokenStream) -> TokenStream {
560561 let name = & ast. ident ;
561562 let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
562563
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) ;
570565
571566 let impl_ = quote ! {
572567 impl #import:: NumCast for #name {
@@ -576,11 +571,7 @@ pub fn num_cast(input: TokenStream) -> TokenStream {
576571 }
577572 } ;
578573
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 ( )
584575}
585576
586577/// Derives [`num_traits::Zero`][zero] for newtypes. The inner type must already implement `Zero`.
@@ -592,13 +583,7 @@ pub fn zero(input: TokenStream) -> TokenStream {
592583 let name = & ast. ident ;
593584 let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
594585
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) ;
602587
603588 let impl_ = quote ! {
604589 impl #import:: Zero for #name {
@@ -611,11 +596,7 @@ pub fn zero(input: TokenStream) -> TokenStream {
611596 }
612597 } ;
613598
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 ( )
619600}
620601
621602/// Derives [`num_traits::One`][one] for newtypes. The inner type must already implement `One`.
@@ -627,13 +608,7 @@ pub fn one(input: TokenStream) -> TokenStream {
627608 let name = & ast. ident ;
628609 let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
629610
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) ;
637612
638613 let impl_ = quote ! {
639614 impl #import:: One for #name {
@@ -646,11 +621,7 @@ pub fn one(input: TokenStream) -> TokenStream {
646621 }
647622 } ;
648623
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 ( )
654625}
655626
656627/// Derives [`num_traits::Num`][num] for newtypes. The inner type must already implement `Num`.
@@ -662,13 +633,7 @@ pub fn num(input: TokenStream) -> TokenStream {
662633 let name = & ast. ident ;
663634 let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
664635
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) ;
672637
673638 let impl_ = quote ! {
674639 impl #import:: Num for #name {
@@ -679,11 +644,7 @@ pub fn num(input: TokenStream) -> TokenStream {
679644 }
680645 } ;
681646
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 ( )
687648}
688649
689650/// Derives [`num_traits::Float`][float] for newtypes. The inner type must already implement
@@ -696,13 +657,7 @@ pub fn float(input: TokenStream) -> TokenStream {
696657 let name = & ast. ident ;
697658 let inner_ty = newtype_inner ( & ast. data ) . expect ( NEWTYPE_ONLY ) ;
698659
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) ;
706661
707662 let impl_ = quote ! {
708663 impl #import:: Float for #name {
@@ -881,9 +836,5 @@ pub fn float(input: TokenStream) -> TokenStream {
881836 }
882837 } ;
883838
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 ( )
889840}
0 commit comments