From de54d59d8ef199619a94a1061a31fcd684ccc5c0 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Wed, 28 Aug 2019 09:36:00 -0700 Subject: [PATCH 1/5] Add fmt::Debug constraint to BitFlagNum Otherwise, `T: Debug` isn't enough to imply `BitFlags: Debug`, and this can get in the way of code generated by #[derive(Debug)]. --- enumflags/src/formatting.rs | 1 - enumflags/src/lib.rs | 3 +++ test_suite/tests/formatting.rs | 10 ++++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/enumflags/src/formatting.rs b/enumflags/src/formatting.rs index 9ef801c..ca2c951 100644 --- a/enumflags/src/formatting.rs +++ b/enumflags/src/formatting.rs @@ -4,7 +4,6 @@ use crate::{BitFlags, _internal::RawBitFlags}; impl fmt::Debug for BitFlags where T: RawBitFlags + fmt::Debug, - T::Type: fmt::Binary + fmt::Debug, { fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { let name = T::bitflags_type_name(); diff --git a/enumflags/src/lib.rs b/enumflags/src/lib.rs index 4d905e1..fa44517 100644 --- a/enumflags/src/lib.rs +++ b/enumflags/src/lib.rs @@ -89,6 +89,7 @@ pub mod _internal { use core::ops::{BitAnd, BitOr, BitXor, Not}; use core::cmp::PartialOrd; + use core::fmt; pub trait BitFlagNum : Default @@ -97,6 +98,8 @@ pub mod _internal { + BitXor + Not + PartialOrd + + fmt::Debug + + fmt::Binary + Copy + Clone { } diff --git a/test_suite/tests/formatting.rs b/test_suite/tests/formatting.rs index 708268b..8f9358e 100644 --- a/test_suite/tests/formatting.rs +++ b/test_suite/tests/formatting.rs @@ -72,3 +72,13 @@ fn format() { "0x0F" ); } + +#[test] +fn debug_generic() { + use enumflags2::{BitFlags, _internal::RawBitFlags}; + + #[derive(Debug)] + struct Debug(BitFlags); + + let _ = format!("{:?}", Debug(BitFlags::::all())); +} From 9eb16f3f7b4acb7106db0d9d78732461f0d56fbd Mon Sep 17 00:00:00 2001 From: arcnmx Date: Tue, 27 Aug 2019 13:56:14 -0700 Subject: [PATCH 2/5] impl TryFrom for BitFlags --- enumflags/Cargo.toml | 3 +++ enumflags/src/fallible.rs | 27 ++++++++++++++++++++ enumflags/src/formatting.rs | 2 +- enumflags/src/lib.rs | 49 ++++++++++++++++++++++++++++++++++--- 4 files changed, 77 insertions(+), 4 deletions(-) create mode 100644 enumflags/src/fallible.rs diff --git a/enumflags/Cargo.toml b/enumflags/Cargo.toml index e2248d9..aab66c2 100644 --- a/enumflags/Cargo.toml +++ b/enumflags/Cargo.toml @@ -12,3 +12,6 @@ documentation = "https://docs.rs/enumflags2" [dependencies] enumflags2_derive = { version = "0.6.0", path = "../enumflags_derive" } serde = { version = "^1.0.0", default-features = false, optional = true } + +[features] +std = [] diff --git a/enumflags/src/fallible.rs b/enumflags/src/fallible.rs new file mode 100644 index 0000000..d1f47a8 --- /dev/null +++ b/enumflags/src/fallible.rs @@ -0,0 +1,27 @@ +use core::convert::TryFrom; +use super::{BitFlags, FromBitsError}; +use super::_internal::RawBitFlags; + +macro_rules! impl_try_from { + () => { }; + ($ty:ty, $($tt:tt)*) => { + impl_try_from! { $ty } + impl_try_from! { $($tt)* } + }; + ($ty:ty) => { + impl TryFrom<$ty> for BitFlags + where + T: RawBitFlags, + { + type Error = FromBitsError; + + fn try_from(bits: T::Type) -> Result { + Self::try_from_bits(bits) + } + } + }; +} + +impl_try_from! { + u8, u16, u32, u64, usize +} diff --git a/enumflags/src/formatting.rs b/enumflags/src/formatting.rs index ca2c951..926eb6c 100644 --- a/enumflags/src/formatting.rs +++ b/enumflags/src/formatting.rs @@ -1,5 +1,5 @@ use core::fmt::{self, Debug, Binary}; -use crate::{BitFlags, _internal::RawBitFlags}; +use crate::{BitFlags, FromBitsError, _internal::RawBitFlags}; impl fmt::Debug for BitFlags where diff --git a/enumflags/src/lib.rs b/enumflags/src/lib.rs index fa44517..32778b8 100644 --- a/enumflags/src/lib.rs +++ b/enumflags/src/lib.rs @@ -44,10 +44,11 @@ //! ## Optional Feature Flags //! //! - [`serde`](https://serde.rs/) implements `Serialize` and `Deserialize` for `BitFlags`. +//! - `std` implements `std::error::Error` for `FromBitsError`. #![warn(missing_docs)] -#![cfg_attr(not(test), no_std)] +#![cfg_attr(all(not(test), not(feature = "std")), no_std)] -#[cfg(test)] +#[cfg(all(test, not(feature = "std")))] extern crate core; use core::{cmp, ops}; use core::iter::FromIterator; @@ -119,6 +120,9 @@ pub mod _internal { // Internal debug formatting implementations mod formatting; +// impl TryFrom for BitFlags +mod fallible; + use _internal::RawBitFlags; /// Represents a set of flags of some type `T`. @@ -151,7 +155,7 @@ where impl From for BitFlags { fn from(t: T) -> BitFlags { - BitFlags { val: t.bits() } + Self::from_flag(t) } } @@ -204,6 +208,22 @@ where } } + pub fn from_flag(t: T) -> Self { + BitFlags { val: t.bits() } + } + + pub fn try_from_bits(bits: T::Type) -> Result> { + let flags = Self::from_bits_truncate(bits); + if flags.bits() == bits { + Ok(flags) + } else { + Err(FromBitsError { + flags, + invalid: bits & !flags.bits(), + }) + } + } + /// Truncates flags that are illegal pub fn from_bits_truncate(bits: T::Type) -> Self { unsafe { BitFlags::new(bits & T::all()) } @@ -361,3 +381,26 @@ mod impl_serde { } } } + +#[derive(Debug, Copy, Clone)] +pub struct FromBitsError { + flags: BitFlags, + invalid: T::Type, +} + +impl FromBitsError { + pub fn truncate(self) -> BitFlags { + self.flags + } + + pub fn invalid_bits(self) -> T::Type { + self.invalid + } +} + +#[cfg(feature = "std")] +impl std::error::Error for FromBitsError { + fn description(&self) -> &str { + "invalid bit representation" + } +} From e3cf71f3bdae87a099703418f08f19745314b19b Mon Sep 17 00:00:00 2001 From: arcnmx Date: Wed, 28 Aug 2019 19:05:35 -0700 Subject: [PATCH 3/5] move try_from_bits and FromBitsError to fallible module --- enumflags/src/fallible.rs | 35 +++++++++++++++++++++++++++++++++-- enumflags/src/lib.rs | 36 +----------------------------------- 2 files changed, 34 insertions(+), 37 deletions(-) diff --git a/enumflags/src/fallible.rs b/enumflags/src/fallible.rs index d1f47a8..ddd5485 100644 --- a/enumflags/src/fallible.rs +++ b/enumflags/src/fallible.rs @@ -1,5 +1,5 @@ use core::convert::TryFrom; -use super::{BitFlags, FromBitsError}; +use super::BitFlags; use super::_internal::RawBitFlags; macro_rules! impl_try_from { @@ -16,7 +16,15 @@ macro_rules! impl_try_from { type Error = FromBitsError; fn try_from(bits: T::Type) -> Result { - Self::try_from_bits(bits) + let flags = Self::from_bits_truncate(bits); + if flags.bits() == bits { + Ok(flags) + } else { + Err(FromBitsError { + flags, + invalid: bits & !flags.bits(), + }) + } } } }; @@ -25,3 +33,26 @@ macro_rules! impl_try_from { impl_try_from! { u8, u16, u32, u64, usize } + +#[derive(Debug, Copy, Clone)] +pub struct FromBitsError { + flags: BitFlags, + invalid: T::Type, +} + +impl FromBitsError { + pub fn truncate(self) -> BitFlags { + self.flags + } + + pub fn invalid_bits(self) -> T::Type { + self.invalid + } +} + +#[cfg(feature = "std")] +impl std::error::Error for FromBitsError { + fn description(&self) -> &str { + "invalid bit representation" + } +} diff --git a/enumflags/src/lib.rs b/enumflags/src/lib.rs index 32778b8..6ff0443 100644 --- a/enumflags/src/lib.rs +++ b/enumflags/src/lib.rs @@ -122,6 +122,7 @@ mod formatting; // impl TryFrom for BitFlags mod fallible; +pub use fallible::FromBitsError; use _internal::RawBitFlags; @@ -212,18 +213,6 @@ where BitFlags { val: t.bits() } } - pub fn try_from_bits(bits: T::Type) -> Result> { - let flags = Self::from_bits_truncate(bits); - if flags.bits() == bits { - Ok(flags) - } else { - Err(FromBitsError { - flags, - invalid: bits & !flags.bits(), - }) - } - } - /// Truncates flags that are illegal pub fn from_bits_truncate(bits: T::Type) -> Self { unsafe { BitFlags::new(bits & T::all()) } @@ -381,26 +370,3 @@ mod impl_serde { } } } - -#[derive(Debug, Copy, Clone)] -pub struct FromBitsError { - flags: BitFlags, - invalid: T::Type, -} - -impl FromBitsError { - pub fn truncate(self) -> BitFlags { - self.flags - } - - pub fn invalid_bits(self) -> T::Type { - self.invalid - } -} - -#[cfg(feature = "std")] -impl std::error::Error for FromBitsError { - fn description(&self) -> &str { - "invalid bit representation" - } -} From 8861464e4859e7de7eebe18e6bd38fd6a5c8e550 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20K=C4=85dzio=C5=82ka?= Date: Thu, 29 Aug 2019 16:50:54 +0200 Subject: [PATCH 4/5] Simplify the impl_try_from macro --- enumflags/src/fallible.rs | 39 ++++++++++++++++++--------------------- 1 file changed, 18 insertions(+), 21 deletions(-) diff --git a/enumflags/src/fallible.rs b/enumflags/src/fallible.rs index ddd5485..5d87231 100644 --- a/enumflags/src/fallible.rs +++ b/enumflags/src/fallible.rs @@ -3,30 +3,27 @@ use super::BitFlags; use super::_internal::RawBitFlags; macro_rules! impl_try_from { - () => { }; - ($ty:ty, $($tt:tt)*) => { - impl_try_from! { $ty } - impl_try_from! { $($tt)* } - }; - ($ty:ty) => { - impl TryFrom<$ty> for BitFlags - where - T: RawBitFlags, - { - type Error = FromBitsError; + ($($ty:ty),*) => { + $( + impl TryFrom<$ty> for BitFlags + where + T: RawBitFlags, + { + type Error = FromBitsError; - fn try_from(bits: T::Type) -> Result { - let flags = Self::from_bits_truncate(bits); - if flags.bits() == bits { - Ok(flags) - } else { - Err(FromBitsError { - flags, - invalid: bits & !flags.bits(), - }) + fn try_from(bits: T::Type) -> Result { + let flags = Self::from_bits_truncate(bits); + if flags.bits() == bits { + Ok(flags) + } else { + Err(FromBitsError { + flags, + invalid: bits & !flags.bits(), + }) + } } } - } + )* }; } From 8499f0c25ff12da86d4f6c6a73d98bf596a309b7 Mon Sep 17 00:00:00 2001 From: arcnmx Date: Thu, 29 Aug 2019 08:41:37 -0700 Subject: [PATCH 5/5] fix std::error::Error impl --- enumflags/src/fallible.rs | 11 +++++++++-- enumflags/src/formatting.rs | 2 +- enumflags/src/lib.rs | 2 +- 3 files changed, 11 insertions(+), 4 deletions(-) diff --git a/enumflags/src/fallible.rs b/enumflags/src/fallible.rs index 5d87231..2520d7b 100644 --- a/enumflags/src/fallible.rs +++ b/enumflags/src/fallible.rs @@ -1,4 +1,5 @@ use core::convert::TryFrom; +use core::fmt; use super::BitFlags; use super::_internal::RawBitFlags; @@ -47,9 +48,15 @@ impl FromBitsError { } } +impl fmt::Display for FromBitsError { + fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result { + write!(fmt, "Invalid bits for {:?}: {:#b}", self.flags, self.invalid) + } +} + #[cfg(feature = "std")] -impl std::error::Error for FromBitsError { +impl std::error::Error for FromBitsError { fn description(&self) -> &str { - "invalid bit representation" + "invalid bitflags representation" } } diff --git a/enumflags/src/formatting.rs b/enumflags/src/formatting.rs index 926eb6c..ca2c951 100644 --- a/enumflags/src/formatting.rs +++ b/enumflags/src/formatting.rs @@ -1,5 +1,5 @@ use core::fmt::{self, Debug, Binary}; -use crate::{BitFlags, FromBitsError, _internal::RawBitFlags}; +use crate::{BitFlags, _internal::RawBitFlags}; impl fmt::Debug for BitFlags where diff --git a/enumflags/src/lib.rs b/enumflags/src/lib.rs index 6ff0443..802478b 100644 --- a/enumflags/src/lib.rs +++ b/enumflags/src/lib.rs @@ -48,7 +48,7 @@ #![warn(missing_docs)] #![cfg_attr(all(not(test), not(feature = "std")), no_std)] -#[cfg(all(test, not(feature = "std")))] +#[cfg(any(test, feature = "std"))] extern crate core; use core::{cmp, ops}; use core::iter::FromIterator;