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" + } +}