Skip to content

Commit fe5149c

Browse files
committed
impl TryFrom<T::Type> for BitFlags<T>
1 parent a4efc89 commit fe5149c

File tree

4 files changed

+77
-4
lines changed

4 files changed

+77
-4
lines changed

enumflags/Cargo.toml

+3
Original file line numberDiff line numberDiff line change
@@ -12,3 +12,6 @@ documentation = "https://docs.rs/enumflags2"
1212
[dependencies]
1313
enumflags2_derive = { version = "0.6.0", path = "../enumflags_derive" }
1414
serde = { version = "^1.0.0", default-features = false, optional = true }
15+
16+
[features]
17+
std = []

enumflags/src/fallible.rs

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
use core::convert::TryFrom;
2+
use super::{BitFlags, FromBitsError};
3+
use super::_internal::RawBitFlags;
4+
5+
macro_rules! impl_try_from {
6+
() => { };
7+
($ty:ty, $($tt:tt)*) => {
8+
impl_try_from! { $ty }
9+
impl_try_from! { $($tt)* }
10+
};
11+
($ty:ty) => {
12+
impl<T> TryFrom<$ty> for BitFlags<T>
13+
where
14+
T: RawBitFlags<Type=$ty>,
15+
{
16+
type Error = FromBitsError<T>;
17+
18+
fn try_from(bits: T::Type) -> Result<Self, Self::Error> {
19+
Self::try_from_bits(bits)
20+
}
21+
}
22+
};
23+
}
24+
25+
impl_try_from! {
26+
u8, u16, u32, u64, usize
27+
}

enumflags/src/formatting.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
use core::fmt::{self, Debug, Binary};
2-
use crate::{BitFlags, _internal::RawBitFlags};
2+
use crate::{BitFlags, FromBitsError, _internal::RawBitFlags};
33

44
impl<T> fmt::Debug for BitFlags<T>
55
where

enumflags/src/lib.rs

+46-3
Original file line numberDiff line numberDiff line change
@@ -44,10 +44,11 @@
4444
//! ## Optional Feature Flags
4545
//!
4646
//! - [`serde`](https://serde.rs/) implements `Serialize` and `Deserialize` for `BitFlags<T>`.
47+
//! - `std` implements `std::error::Error` for `FromBitsError`.
4748
#![warn(missing_docs)]
48-
#![cfg_attr(not(test), no_std)]
49+
#![cfg_attr(all(not(test), not(feature = "std")), no_std)]
4950

50-
#[cfg(test)]
51+
#[cfg(all(test, not(feature = "std")))]
5152
extern crate core;
5253
use core::{cmp, ops};
5354
use core::iter::FromIterator;
@@ -118,6 +119,9 @@ pub mod _internal {
118119
// Internal debug formatting implementations
119120
mod formatting;
120121

122+
// impl TryFrom<T::Type> for BitFlags<T>
123+
mod fallible;
124+
121125
use _internal::RawBitFlags;
122126

123127
/// Represents a set of flags of some type `T`.
@@ -150,7 +154,7 @@ where
150154

151155
impl<T: RawBitFlags> From<T> for BitFlags<T> {
152156
fn from(t: T) -> BitFlags<T> {
153-
BitFlags { val: t.bits() }
157+
Self::from_flag(t)
154158
}
155159
}
156160

@@ -203,6 +207,22 @@ where
203207
}
204208
}
205209

210+
pub fn from_flag(t: T) -> Self {
211+
BitFlags { val: t.bits() }
212+
}
213+
214+
pub fn try_from_bits(bits: T::Type) -> Result<Self, FromBitsError<T>> {
215+
let flags = Self::from_bits_truncate(bits);
216+
if flags.bits() == bits {
217+
Ok(flags)
218+
} else {
219+
Err(FromBitsError {
220+
flags,
221+
invalid: bits & !flags.bits(),
222+
})
223+
}
224+
}
225+
206226
/// Truncates flags that are illegal
207227
pub fn from_bits_truncate(bits: T::Type) -> Self {
208228
unsafe { BitFlags::new(bits & T::all()) }
@@ -360,3 +380,26 @@ mod impl_serde {
360380
}
361381
}
362382
}
383+
384+
#[derive(Debug, Copy, Clone)]
385+
pub struct FromBitsError<T: RawBitFlags> {
386+
flags: BitFlags<T>,
387+
invalid: T::Type,
388+
}
389+
390+
impl<T: RawBitFlags> FromBitsError<T> {
391+
pub fn truncate(self) -> BitFlags<T> {
392+
self.flags
393+
}
394+
395+
pub fn invalid_bits(self) -> T::Type {
396+
self.invalid
397+
}
398+
}
399+
400+
#[cfg(feature = "std")]
401+
impl<T: RawBitFlags> std::error::Error for FromBitsError<T> {
402+
fn description(&self) -> &str {
403+
"invalid bit representation"
404+
}
405+
}

0 commit comments

Comments
 (0)