Skip to content

Commit d4cc8f7

Browse files
committed
Consolidate the code to find num_traits
1 parent 4ac994e commit d4cc8f7

File tree

1 file changed

+57
-106
lines changed

1 file changed

+57
-106
lines changed

src/lib.rs

Lines changed: 57 additions & 106 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@
6666
extern crate proc_macro;
6767

6868
use proc_macro::TokenStream;
69-
use proc_macro2::Span;
69+
use proc_macro2::{Span, TokenStream as TokenStream2};
7070
use quote::quote;
7171
use 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

499500
const 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

Comments
 (0)