-
Notifications
You must be signed in to change notification settings - Fork 701
Add From and TryFrom impls in libc_enum macro #1088
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
Changes from all commits
a0dd24c
aedd467
367a4d6
1a14711
14de743
f9b344d
84a6620
c225ee6
e4b4e91
fa5641f
b2dcb49
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,12 +1,14 @@ | ||
#[cfg(target_os = "dragonfly")] | ||
extern crate cc; | ||
extern crate version_check as rustc; | ||
|
||
#[cfg(target_os = "dragonfly")] | ||
fn main() { | ||
#[cfg(target_os = "dragonfly")] | ||
cc::Build::new() | ||
.file("src/errno_dragonfly.c") | ||
.compile("liberrno_dragonfly.a"); | ||
} | ||
|
||
#[cfg(not(target_os = "dragonfly"))] | ||
fn main() {} | ||
if rustc::is_min_version("1.34.0").unwrap_or(false) { | ||
println!("cargo:rustc-cfg=try_from"); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -2,8 +2,6 @@ | |
/// with values from the libc crate. It is used the same way as the `bitflags!` macro, except | ||
/// that only the name of the flag value has to be given. | ||
/// | ||
/// The `libc` crate must be in scope with the name `libc`. | ||
/// | ||
/// # Example | ||
/// ``` | ||
/// libc_bitflags!{ | ||
|
@@ -53,204 +51,121 @@ macro_rules! libc_bitflags { | |
pub struct $BitFlags: $T { | ||
$( | ||
$(#[$inner $($args)*])* | ||
const $Flag = libc::$Flag $(as $cast)*; | ||
const $Flag = ::libc::$Flag $(as $cast)*; | ||
)+ | ||
} | ||
} | ||
}; | ||
} | ||
|
||
/// The `libc_enum!` macro helps with a common use case of defining an enum exclusively using | ||
/// values from the `libc` crate. This macro supports both `pub` and private `enum`s. | ||
/// values from the `libc` crate. The type after the enum name specifies the type of the constants | ||
/// in `libc`. The macro will generate impls of `From` and `TryFrom` to convert between numeric and | ||
/// enum values. | ||
/// | ||
/// `TryFrom` is only implemented for Rust >= 1.34.0, where the trait is stable. An equivalent | ||
/// `try_from` inherent method is made available regardless of the Rust version. `TryInto` should | ||
/// not be used as long as the MSRV for nix is less than 1.34.0. | ||
/// | ||
/// | ||
/// The `libc` crate must be in scope with the name `libc`. | ||
/// Documentation for each variant must be provided before any cfg attributes. | ||
/// | ||
/// # Example | ||
/// ``` | ||
/// libc_enum!{ | ||
/// pub enum ProtFlags { | ||
/// libc_enum! { | ||
/// pub enum ProtFlags: c_int { | ||
/// PROT_NONE, | ||
/// PROT_READ, | ||
/// PROT_WRITE, | ||
/// PROT_EXEC, | ||
/// /// Documentation before cfg attribute. | ||
/// #[cfg(any(target_os = "linux", target_os = "android"))] | ||
/// PROT_GROWSDOWN, | ||
/// #[cfg(any(target_os = "linux", target_os = "android"))] | ||
/// PROT_GROWSUP, | ||
/// } | ||
/// } | ||
/// | ||
/// let flag: c_int = ProtFlags::PROT_NONE.into(); | ||
/// let flag: ProtFlags = ProtFlags::try_from(::libc::PROT_NONE).unwrap(); | ||
/// ``` | ||
macro_rules! libc_enum { | ||
// (non-pub) Exit rule. | ||
(@make_enum | ||
{ | ||
name: $BitFlags:ident, | ||
attrs: [$($attrs:tt)*], | ||
entries: [$($entries:tt)*], | ||
} | ||
) => { | ||
$($attrs)* | ||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | ||
enum $BitFlags { | ||
$($entries)* | ||
} | ||
}; | ||
|
||
// (pub) Exit rule. | ||
(@make_enum | ||
{ | ||
pub, | ||
name: $BitFlags:ident, | ||
attrs: [$($attrs:tt)*], | ||
entries: [$($entries:tt)*], | ||
} | ||
) => { | ||
$($attrs)* | ||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | ||
pub enum $BitFlags { | ||
$($entries)* | ||
} | ||
}; | ||
|
||
// (non-pub) Done accumulating. | ||
(@accumulate_entries | ||
{ | ||
name: $BitFlags:ident, | ||
attrs: $attrs:tt, | ||
}, | ||
$entries:tt; | ||
// pub | ||
( | ||
$(#[$enum_attr:meta])* | ||
pub $(($($scope:tt)*))* enum $($def:tt)* | ||
) => { | ||
libc_enum! { | ||
@make_enum | ||
{ | ||
name: $BitFlags, | ||
attrs: $attrs, | ||
entries: $entries, | ||
} | ||
@(pub $(($($scope)*))*) | ||
$(#[$enum_attr])* | ||
enum $($def)* | ||
} | ||
}; | ||
|
||
// (pub) Done accumulating. | ||
(@accumulate_entries | ||
{ | ||
pub, | ||
name: $BitFlags:ident, | ||
attrs: $attrs:tt, | ||
}, | ||
$entries:tt; | ||
// non-pub | ||
( | ||
$(#[$enum_attr:meta])* | ||
enum $($def:tt)* | ||
) => { | ||
libc_enum! { | ||
@make_enum | ||
{ | ||
pub, | ||
name: $BitFlags, | ||
attrs: $attrs, | ||
entries: $entries, | ||
} | ||
@() | ||
$(#[$enum_attr])* | ||
enum $($def)* | ||
} | ||
}; | ||
|
||
// Munch an attr. | ||
(@accumulate_entries | ||
$prefix:tt, | ||
[$($entries:tt)*]; | ||
#[$attr:meta] $($tail:tt)* | ||
) => { | ||
libc_enum! { | ||
@accumulate_entries | ||
$prefix, | ||
[ | ||
$($entries)* | ||
#[$attr] | ||
]; | ||
$($tail)* | ||
( | ||
@($($vis:tt)*) | ||
$(#[$enum_attr:meta])* | ||
enum $enum:ident : $prim:ty { | ||
$( | ||
$(#[doc = $var_doc:tt])* | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Doc strings and cfg attributes are both different kinds of attributes. Could you simplify the macro by combining these two lines? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The compiler emits a warning for "unused doc comment" when it sees a doc comment annotating a match arm, so I match them separately here so that I can use the |
||
$(#[cfg($var_cfg:meta)])* | ||
$entry:ident | ||
),* $(,)* | ||
} | ||
}; | ||
|
||
// Munch last ident if not followed by a comma. | ||
(@accumulate_entries | ||
$prefix:tt, | ||
[$($entries:tt)*]; | ||
$entry:ident | ||
) => { | ||
libc_enum! { | ||
@accumulate_entries | ||
$prefix, | ||
[ | ||
$($entries)* | ||
$entry = libc::$entry, | ||
]; | ||
$(#[$enum_attr])* | ||
#[derive(Clone, Copy, Debug, Eq, Hash, Ord, PartialEq, PartialOrd)] | ||
$($vis)* enum $enum { | ||
$( | ||
$(#[doc = $var_doc])* | ||
$(#[cfg($var_cfg)])* | ||
$entry = ::libc::$entry as isize | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. why cast to There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Given the way that these types will be used, I think that There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The problem is that in some places we're using a custom type as The reason we need to use a custom type is that in some cases the underlying type depends on the target machine. I'm not sure how we could get around that with the
Can you share an example where the repr makes a difference? |
||
),* | ||
} | ||
}; | ||
|
||
// Munch an ident; covers terminating comma case. | ||
(@accumulate_entries | ||
$prefix:tt, | ||
[$($entries:tt)*]; | ||
$entry:ident, $($tail:tt)* | ||
) => { | ||
libc_enum! { | ||
@accumulate_entries | ||
$prefix, | ||
[ | ||
$($entries)* | ||
$entry = libc::$entry, | ||
]; | ||
$($tail)* | ||
impl $enum { | ||
pub fn try_from(value: $prim) -> std::result::Result<$enum, ::Error> { | ||
match value { | ||
$( | ||
$(#[cfg($var_cfg)])* | ||
::libc::$entry => Ok($enum::$entry), | ||
)* | ||
// don't think this Error is the correct one | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think that's a fine choice of error. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The reason I doubt is that How about adding an There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Yeah, I understand. |
||
_ => Err(::Error::invalid_argument()) | ||
} | ||
} | ||
} | ||
}; | ||
|
||
// Munch an ident and cast it to the given type; covers terminating comma. | ||
(@accumulate_entries | ||
$prefix:tt, | ||
[$($entries:tt)*]; | ||
$entry:ident as $ty:ty, $($tail:tt)* | ||
) => { | ||
libc_enum! { | ||
@accumulate_entries | ||
$prefix, | ||
[ | ||
$($entries)* | ||
$entry = libc::$entry as $ty, | ||
]; | ||
$($tail)* | ||
impl std::convert::From<$enum> for $prim { | ||
fn from(value: $enum) -> $prim { | ||
match value { | ||
$( | ||
$(#[cfg($var_cfg)])* | ||
$enum::$entry => ::libc::$entry, | ||
)* | ||
} | ||
} | ||
} | ||
}; | ||
|
||
// (non-pub) Entry rule. | ||
( | ||
$(#[$attr:meta])* | ||
enum $BitFlags:ident { | ||
$($vals:tt)* | ||
} | ||
) => { | ||
libc_enum! { | ||
@accumulate_entries | ||
{ | ||
name: $BitFlags, | ||
attrs: [$(#[$attr])*], | ||
}, | ||
[]; | ||
$($vals)* | ||
} | ||
}; | ||
#[cfg(try_from)] | ||
impl std::convert::TryFrom<$prim> for $enum { | ||
type Error = ::Error; | ||
|
||
// (pub) Entry rule. | ||
( | ||
$(#[$attr:meta])* | ||
pub enum $BitFlags:ident { | ||
$($vals:tt)* | ||
} | ||
) => { | ||
libc_enum! { | ||
@accumulate_entries | ||
{ | ||
pub, | ||
name: $BitFlags, | ||
attrs: [$(#[$attr])*], | ||
}, | ||
[]; | ||
$($vals)* | ||
fn try_from(value: $prim) -> std::result::Result<$enum, Self::Error> { | ||
$enum::try_from(value) | ||
} | ||
} | ||
}; | ||
} | ||
|
Uh oh!
There was an error while loading. Please reload this page.