Skip to content

Commit 611b376

Browse files
authored
Allow user to set the crate path of num_enum (#77)
Add a `#[num_enum(crate = ..)]` attribute, which lets the user set where the num_enum crate is located.
1 parent 7138fc6 commit 611b376

File tree

6 files changed

+133
-50
lines changed

6 files changed

+133
-50
lines changed

num_enum/tests/try_build/compile_fail/custom_error_type_parsing.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -22,8 +22,8 @@ error: num_enum attribute must have at most one error_type
2222
29 | #[num_enum(error_type(name = CustomError, constructor = CustomError::new), error_type(name = CustomError, constructor = CustomError::new))]
2323
| ^^^^^^^^^^
2424

25-
error: At most one num_enum error_type attribute may be specified
26-
--> tests/try_build/compile_fail/custom_error_type_parsing.rs:39:1
25+
error: num_enum attribute must have at most one error_type
26+
--> tests/try_build/compile_fail/custom_error_type_parsing.rs:39:12
2727
|
2828
39 | #[num_enum(error_type(name = CustomError, constructor = CustomError::new))]
29-
| ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
29+
| ^^^^^^^^^^

num_enum/tests/try_build/compile_fail/variants_with_fields.stderr

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,16 @@
1-
error: `num_enum` only supports unit variants (with no associated data), but `Number::NonZero` was not a unit variant.
1+
error: `::num_enum` only supports unit variants (with no associated data), but `Number::NonZero` was not a unit variant.
22
--> $DIR/variants_with_fields.rs:7:5
33
|
44
7 | NonZero(u8),
55
| ^^^^^^^^^^^
66

7-
error: `num_enum` only supports unit variants (with no associated data), but `Colour::Red` was not a unit variant.
7+
error: `::num_enum` only supports unit variants (with no associated data), but `Colour::Red` was not a unit variant.
88
--> $DIR/variants_with_fields.rs:13:5
99
|
1010
13 | Red { intensity: u8 },
1111
| ^^^^^^^^^^^^^^^^^^^^^
1212

13-
error: `num_enum` only supports unit variants (with no associated data), but `Meaningless::Beep` was not a unit variant.
13+
error: `::num_enum` only supports unit variants (with no associated data), but `Meaningless::Beep` was not a unit variant.
1414
--> $DIR/variants_with_fields.rs:19:5
1515
|
1616
19 | Beep(),

num_enum_derive/Cargo.toml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,7 +35,7 @@ features = ["external_doc"]
3535
proc-macro2 = "1.0.60"
3636
proc-macro-crate = { version = ">= 1, <= 3", optional = true }
3737
quote = "1"
38-
syn = { version = "2", features = ["parsing"] }
38+
syn = { version = "2", features = ["derive", "extra-traits", "parsing"] }
3939

4040
[dev-dependencies]
4141
syn = { version = "2", features = ["extra-traits", "parsing"] }

num_enum_derive/src/enum_attributes.rs

Lines changed: 70 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -13,20 +13,50 @@ mod kw {
1313

1414
// Example: error_type(name = Foo, constructor = Foo::new)
1515
#[cfg_attr(test, derive(Debug))]
16+
#[derive(Default)]
1617
pub(crate) struct Attributes {
1718
pub(crate) error_type: Option<ErrorTypeAttribute>,
19+
pub(crate) crate_path: Option<CrateAttribute>,
1820
}
1921

2022
// Example: error_type(name = Foo, constructor = Foo::new)
2123
#[cfg_attr(test, derive(Debug))]
2224
pub(crate) enum AttributeItem {
2325
ErrorType(ErrorTypeAttribute),
26+
CratePath(CrateAttribute),
27+
}
28+
29+
impl Attributes {
30+
pub(crate) fn exclusive_union(&mut self, other: Self) -> Result<()> {
31+
if self.crate_path.is_some() {
32+
if let Some(other) = &other.crate_path {
33+
return Err(Error::new(
34+
other.span,
35+
"num_enum attribute must have at most one crate",
36+
));
37+
}
38+
} else {
39+
self.crate_path = other.crate_path;
40+
}
41+
if self.error_type.is_some() {
42+
if let Some(other) = &other.error_type {
43+
return Err(Error::new(
44+
other.span,
45+
"num_enum attribute must have at most one error_type",
46+
));
47+
}
48+
} else {
49+
self.error_type = other.error_type;
50+
}
51+
Ok(())
52+
}
2453
}
2554

2655
impl Parse for Attributes {
2756
fn parse(input: ParseStream<'_>) -> Result<Self> {
2857
let attribute_items = input.parse_terminated(AttributeItem::parse, syn::Token![,])?;
2958
let mut maybe_error_type = None;
59+
let mut maybe_krate_path = None;
3060
for attribute_item in &attribute_items {
3161
match attribute_item {
3262
AttributeItem::ErrorType(error_type) => {
@@ -38,10 +68,20 @@ impl Parse for Attributes {
3868
}
3969
maybe_error_type = Some(error_type.clone());
4070
}
71+
AttributeItem::CratePath(krate_path) => {
72+
if maybe_krate_path.is_some() {
73+
return Err(Error::new(
74+
krate_path.span,
75+
"num_enum attribute must have at most one crate",
76+
));
77+
}
78+
maybe_krate_path = Some(krate_path.clone());
79+
}
4180
}
4281
}
4382
Ok(Self {
4483
error_type: maybe_error_type,
84+
crate_path: maybe_krate_path,
4585
})
4686
}
4787
}
@@ -51,6 +91,8 @@ impl Parse for AttributeItem {
5191
let lookahead = input.lookahead1();
5292
if lookahead.peek(kw::error_type) {
5393
input.parse().map(Self::ErrorType)
94+
} else if lookahead.peek(syn::token::Crate) {
95+
input.parse().map(Self::CratePath)
5496
} else {
5597
Err(lookahead.error())
5698
}
@@ -168,6 +210,24 @@ impl Parse for ErrorTypeConstructorAttribute {
168210
}
169211
}
170212

213+
#[derive(Clone)]
214+
#[cfg_attr(test, derive(Debug))]
215+
pub(crate) struct CrateAttribute {
216+
pub(crate) path: syn::Path,
217+
218+
span: Span,
219+
}
220+
221+
impl Parse for CrateAttribute {
222+
fn parse(input: ParseStream) -> Result<Self> {
223+
let span = input.span();
224+
let _: syn::token::Crate = input.parse()?;
225+
let _: syn::token::Eq = input.parse()?;
226+
let path = syn::Path::parse_mod_style(input)?;
227+
Ok(Self { path, span })
228+
}
229+
}
230+
171231
#[cfg(test)]
172232
mod test {
173233
use crate::enum_attributes::Attributes;
@@ -178,11 +238,15 @@ mod test {
178238
fn parse_num_enum_attr() {
179239
let expected_name: Path = parse_quote! { Foo };
180240
let expected_constructor: Path = parse_quote! { ::foo::Foo::<u8>::new };
241+
let expected_krate: Path = parse_quote! { ::num_enum };
181242

182-
let attributes: Attributes =
183-
syn::parse_str("error_type(name = Foo, constructor = ::foo::Foo::<u8>::new)").unwrap();
243+
let attributes: Attributes = syn::parse_str(
244+
"error_type(name = Foo, constructor = ::foo::Foo::<u8>::new), crate = ::num_enum",
245+
)
246+
.unwrap();
184247
assert!(attributes.error_type.is_some());
185248
let error_type = attributes.error_type.unwrap();
249+
let krate_path = attributes.crate_path.unwrap();
186250
assert_eq!(
187251
error_type.name.path.to_token_stream().to_string(),
188252
expected_name.to_token_stream().to_string()
@@ -191,6 +255,10 @@ mod test {
191255
error_type.constructor.path.to_token_stream().to_string(),
192256
expected_constructor.to_token_stream().to_string()
193257
);
258+
assert_eq!(
259+
krate_path.path.to_token_stream().to_string(),
260+
expected_krate.to_token_stream().to_string()
261+
);
194262
}
195263

196264
#[test]

num_enum_derive/src/lib.rs

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ use syn::{parse_macro_input, Expr, Ident};
1010

1111
mod enum_attributes;
1212
mod parsing;
13-
use parsing::{get_crate_name, EnumInfo};
13+
use parsing::{get_crate_path, EnumInfo};
1414
mod utils;
1515
mod variant_attributes;
1616

@@ -90,7 +90,7 @@ pub fn derive_into_primitive(input: TokenStream) -> TokenStream {
9090
#[proc_macro_derive(FromPrimitive, attributes(num_enum, default, catch_all))]
9191
pub fn derive_from_primitive(input: TokenStream) -> TokenStream {
9292
let enum_info: EnumInfo = parse_macro_input!(input);
93-
let krate = Ident::new(&get_crate_name(), Span::call_site());
93+
let krate = get_crate_path(enum_info.crate_path.clone());
9494

9595
let is_naturally_exhaustive = enum_info.is_naturally_exhaustive();
9696
let catch_all_body = match is_naturally_exhaustive {
@@ -124,7 +124,7 @@ pub fn derive_from_primitive(input: TokenStream) -> TokenStream {
124124
debug_assert_eq!(variant_idents.len(), variant_expressions.len());
125125

126126
TokenStream::from(quote! {
127-
impl ::#krate::FromPrimitive for #name {
127+
impl #krate::FromPrimitive for #name {
128128
type Primitive = #repr;
129129

130130
fn from_primitive(number: Self::Primitive) -> Self {
@@ -153,12 +153,12 @@ pub fn derive_from_primitive(input: TokenStream) -> TokenStream {
153153
fn from (
154154
number: #repr,
155155
) -> Self {
156-
::#krate::FromPrimitive::from_primitive(number)
156+
#krate::FromPrimitive::from_primitive(number)
157157
}
158158
}
159159

160160
#[doc(hidden)]
161-
impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
161+
impl #krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
162162
})
163163
}
164164

@@ -190,8 +190,7 @@ pub fn derive_from_primitive(input: TokenStream) -> TokenStream {
190190
#[proc_macro_derive(TryFromPrimitive, attributes(num_enum))]
191191
pub fn derive_try_from_primitive(input: TokenStream) -> TokenStream {
192192
let enum_info: EnumInfo = parse_macro_input!(input);
193-
let krate = Ident::new(&get_crate_name(), Span::call_site());
194-
193+
let krate = get_crate_path(enum_info.crate_path.clone());
195194
let EnumInfo {
196195
ref name,
197196
ref repr,
@@ -209,7 +208,7 @@ pub fn derive_try_from_primitive(input: TokenStream) -> TokenStream {
209208
let error_constructor = &error_type_info.constructor;
210209

211210
TokenStream::from(quote! {
212-
impl ::#krate::TryFromPrimitive for #name {
211+
impl #krate::TryFromPrimitive for #name {
213212
type Primitive = #repr;
214213
type Error = #error_type;
215214

@@ -251,12 +250,12 @@ pub fn derive_try_from_primitive(input: TokenStream) -> TokenStream {
251250
number: #repr,
252251
) -> ::core::result::Result<Self, #error_type>
253252
{
254-
::#krate::TryFromPrimitive::try_from_primitive(number)
253+
#krate::TryFromPrimitive::try_from_primitive(number)
255254
}
256255
}
257256

258257
#[doc(hidden)]
259-
impl ::#krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
258+
impl #krate::CannotDeriveBothFromPrimitiveAndTryFromPrimitive for #name {}
260259
})
261260
}
262261

@@ -302,14 +301,14 @@ pub fn derive_try_from_primitive(input: TokenStream) -> TokenStream {
302301
#[proc_macro_derive(UnsafeFromPrimitive, attributes(num_enum))]
303302
pub fn derive_unsafe_from_primitive(stream: TokenStream) -> TokenStream {
304303
let enum_info = parse_macro_input!(stream as EnumInfo);
305-
let krate = Ident::new(&get_crate_name(), Span::call_site());
304+
let krate = get_crate_path(enum_info.crate_path);
306305

307306
let EnumInfo {
308307
ref name, ref repr, ..
309308
} = enum_info;
310309

311310
TokenStream::from(quote! {
312-
impl ::#krate::UnsafeFromPrimitive for #name {
311+
impl #krate::UnsafeFromPrimitive for #name {
313312
type Primitive = #repr;
314313

315314
unsafe fn unchecked_transmute_from(number: Self::Primitive) -> Self {

0 commit comments

Comments
 (0)