-
Notifications
You must be signed in to change notification settings - Fork 34
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Allow user to set the crate path of num_enum #77
base: main
Are you sure you want to change the base?
Conversation
This closes #76 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great, thanks for the quick PR! 🙏
I have some nits here and there, but otherwise LGTM ✅
num_enum/tests/try_from_primitive.rs
Outdated
mod reexport { | ||
pub use ::num_enum; | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Could you have this on its own tests/file.rs
, and then, use, instead:
mod reexport { | |
pub use ::num_enum; | |
} | |
extern crate std as num_enum; // ensure `::num_enum` is a bad path | |
mod reexport { | |
pub extern crate num_enum; | |
} |
This way if the macro decided to ignore the crate
parameter, we will detect it and fail 🙂
num_enum_derive/src/lib.rs
Outdated
let s: syn::LitStr = input.parse()?; | ||
let path = s.parse_with(syn::Path::parse_mod_style)?; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
let s: syn::LitStr = input.parse()?; | |
let path = s.parse_with(syn::Path::parse_mod_style)?; | |
let path = syn::Path::parse_mod_style(input)?; |
The fact serde
uses a literal on the rhs of crate = …
is a historical artifact (crate = ::some_path
was not legal at the beginning), but nowadays not requiring code to be stringified is better for spans and IDEs 🙂
num_enum_derive/src/lib.rs
Outdated
for attr in attrs { | ||
if attr.path.is_ident("num_enum") { | ||
match attr.parse_args_with(NumEnumAttributes::parse) { | ||
Ok(NumEnumAttributes { items }) => { | ||
if items.len() != 1 || crate_path.is_some() { | ||
die!(attr.tokens => "Expected exactly one `crate` argument"); | ||
} else { | ||
let NumEnumAttributeItem::Crate(item) = | ||
items.into_iter().next().unwrap(); | ||
crate_path = Some(item.path); | ||
} | ||
} | ||
Err(err) => { | ||
die!(attr => | ||
format!("Invalid attribute: {}", err) | ||
); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This won't "scale well" if other attributes are added. I'd rather suggest an approach along the lines of .flat_map()
ing / nested-for
-style the attributes (since #[num_enum(foo)] #[num_enum(bar)]
is the same as #[num_enum(foo, bar)]
:
for attr in attrs {
if attr.path.is_ident("num_enum") {
for item in attr.parse_args_with(NumEnumAttributes::parse)? {
match item {
// when written like this, it's easier to extend with more attrs.
NumEnumAttributeItem::Crate(item) => {
if crate_path.is_some() {
die!(item.path => "duplicate `crate` annotation");
}
crate_path = Some(item.path);
},
}
}
}
…
}
num_enum_derive/src/lib.rs
Outdated
syn::LitStr::new(&format!("::{}", get_crate_name()), Span::call_site()) | ||
.parse_with(syn::Path::parse_mod_style) | ||
.unwrap() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
syn::LitStr::new(&format!("::{}", get_crate_name()), Span::call_site()) | |
.parse_with(syn::Path::parse_mod_style) | |
.unwrap() | |
let krate = get_crate_name(); | |
parse_quote!( :: #krate ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this parse_quote!
would generate :: "num_enum"
because krate is a String
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
maybe let krate = format_ident!("{}", get_crate_name());
?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah true, yeah what you've just suggested would be the right thing.
Although at that point, I guess we could go back to something closer to your original suggestion:
syn::parse_str(&format!("::{}", get_crate_name())).unwrap()
- (it just sidesteps the intermediary
LitStr
)
num_enum_derive/src/lib.rs
Outdated
let krate = enum_info.crate_path.clone().unwrap_or_else(|| { | ||
syn::LitStr::new(&format!("::{}", get_crate_name()), Span::call_site()) | ||
.parse_with(syn::Path::parse_mod_style) | ||
.unwrap() | ||
}); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Hmm, given the code duplication, I wonder if get_crate_name()
shouldn't be taking a Option<Path>
parameter and being the one emitting the actual path?
Add a `#[num_enum(crate = ..)]` attribute, which lets the user set where the num_enum crate is located.
@danielhenrymantilla hey, sorry for taking a while to get back to this. i think i have now addressed most of the points. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Great!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM, thanks! I have suggestions for a few very small tweaks, then let's merge!
@@ -0,0 +1,5 @@ | |||
error: Expected exactly one `crate` argument |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd expect this error message to be "at most one" rather than "exactly one" - it's allowed to not specify a crate
argument.
@@ -1,16 +1,16 @@ | |||
error: `num_enum` only supports unit variants (with no associated data), but `Number::NonZero` was not a unit variant. | |||
error: `:: num_enum` only supports unit variants (with no associated data), but `Number::NonZero` was not a unit variant. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I wouldn't expect a space between ::
and num_enum
here?
match item { | ||
NumEnumAttributeItem::Crate(item) => { | ||
if crate_path.is_some() { | ||
die!(attr.tokens => "Expected exactly one `crate` argument"); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
die!(attr.tokens => "Expected exactly one `crate` argument"); | |
die!(attr.tokens => "Expected at most one `crate` argument"); |
proc_macro_crate::FoundCrate::Itself => parse_quote!(::num_enum), | ||
proc_macro_crate::FoundCrate::Name(name) => { | ||
let krate = format_ident!("{}", name); | ||
parse_quote!( :: #krate ) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
parse_quote!( :: #krate ) | |
parse_quote!( ::#krate ) |
// Don't depend on proc-macro-crate in no_std environments because it causes an awkward dependency | ||
// on serde with std. | ||
// | ||
// no_std dependees on num_enum cannot rename the num_enum crate when they depend on it. Sorry. | ||
// | ||
// See https://github.com/illicitonion/num_enum/issues/18 | ||
#[cfg(not(feature = "proc-macro-crate"))] | ||
fn get_crate_name() -> String { | ||
String::from("num_enum") | ||
fn get_crate_name(path: Option<syn::Path>) -> syn::Path { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
fn get_crate_name(path: Option<syn::Path>) -> syn::Path { | |
fn get_crate_path(path: Option<syn::Path>) -> syn::Path { |
Let's rename this (and the other #[cfg
-gated one, and its call sites) to make more clear that it has the ::
prefix (and that callers don't need to add it)
Can I help in any way to get this over the finish line and get this feature released? |
Add a
#[num_enum(crate = ..)]
attribute, which lets the user set where the num_enum crate is located.