Skip to content
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

Maintainership #20

Closed
wants to merge 9 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
174 changes: 31 additions & 143 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,24 +1,20 @@
# Unmaintained

If anyone wants to maintain it, open an issue.

[![LICENSE](https://img.shields.io/badge/license-MIT-blue.svg)](LICENSE-MIT)
[![LICENSE](https://img.shields.io/badge/license-apache-blue.svg)](LICENSE-APACHE)
[![Documentation](https://docs.rs/enumflags/badge.svg)](https://docs.rs/enumflags)
[![Crates.io Version](https://img.shields.io/crates/v/enumflags.svg)](https://crates.io/crates/enumflags)


# Bitflags
# Enumflags

## Usage

Cargo.toml
In your `Cargo.toml`:
```Toml
[dependencies]
enumflags = "*"
enumflags_derive = "*"
enumflags = "^0.4"
enumflags_derive = "^0.4"
```

If using the 2015 Rust edition, add this to your crate root:
```Rust
extern crate enumflags;
#[macro_use]
Expand All @@ -27,160 +23,52 @@ extern crate enumflags_derive;

## Features

- [x] Uses enums to represent BitFlags.
- [x] Uses enums to represent individual flags—a set of flags is a separate type from a single flag.
- [x] Detects incorrect BitFlags at compile time.
- Non-unique bits.
- Missing values.
- Flags larger than the chosen `repr`.
- [x] Has a similar API compared to the popular [bitflags](https://crates.io/crates/bitflags) crate.
- [x] Does not expose the generated types explicity. The user interacts exclusivly with `struct BitFields<Enum>;`.
- [x] Prints the binary flag value as well as the flag enums `BitFlags { 0b1111, Flags::[A, B, C, D] }`.

#### Detects incorrect flags values

```Rust
#[derive(EnumFlags, Copy, Clone, Debug)]
#[repr(u8)]
pub enum Test {
A = 0b0001,
B = 0b0010,
C = 0b0101,
D = 0b1000,
}
```

Error:
```Rust
error: custom derive attribute panicked
--> src/main.rs:6:10
|
6 | #[derive(EnumFlags, Copy, Clone, Debug)]
| ^^^^^^^^^
|
= help: message: The following flags are not unique: ["Test::A = 0b1", "Test::C = 0b101"]
```


#### Detects flags that are too big
```Rust
#[derive(EnumFlags, Copy, Clone, Debug)]
#[repr(u8)]
pub enum Test {
A = 0b0001,
B = 0b0010,
C = 0b0100,
D = 0b100000000,
}
```
- [x] Does not expose the generated types explicity. The user interacts exclusively with `struct BitFlags<Enum>;`.
- [x] The debug formatter prints the binary flag value as well as the flag enums: `BitFlags(0b1111, [A, B, C, D])`.

Error:
```Rust
error: custom derive attribute panicked
--> src/main.rs:6:10
|
6 | #[derive(EnumFlags, Copy, Clone, Debug)]
| ^^^^^^^^^
|
= help: message: Value '0b100000000' is too big for an u8
```

#### Detects missing flags
### Example

```Rust
#[derive(EnumFlags, Copy, Clone, Debug)]
#[repr(u8)]
pub enum Test {
A = 0b0001,
B = 0b0010,
C,
D = 0b1000,
}
```

Error:
```Rust
error: custom derive attribute panicked
--> src/main.rs:6:10
|
6 | #[derive(EnumFlags, Copy, Clone, Debug)]
| ^^^^^^^^^
|
= help: message: At least one variant was not initialized explicity with a value.
```


```Rust
```rust
extern crate enumflags;
#[macro_use]
extern crate enumflags_derive;
use enumflags::*;

#[derive(EnumFlags, Copy, Clone, Debug)]
use enumflags::BitFlags;

#[derive(EnumFlags, Copy, Clone, Debug, PartialEq)]
#[repr(u8)]
pub enum Test {
enum Test {
A = 0b0001,
B = 0b0010,
C = 0b0100,
D = 0b1000,
}

fn print_test<B: Into<BitFlags<Test>>>(bitflag: B) {
println!("{:?}", bitflag.into());
}

fn main() {
// BitFlags { 0b1111, Flags::[A, B, C, D] }
print_test(BitFlags::<Test>::all());

// BitFlags { 0b1111, Flags::[A, B, C, D] }
print_test(BitFlags::all());

// BitFlags { 0b0, Flags::[] }
print_test(BitFlags::empty());

// BitFlags { 0b101, Flags::[A, C] }
print_test(BitFlags::from_bits_truncate(5));

// BitFlags { 0b100, Flags::[C] }
print_test(BitFlags::from_bits_truncate(4));

// BitFlags { 0b0, Flags::[] }
print_test(BitFlags::from_bits(16).unwrap_or(BitFlags::empty()));

// BitFlags { 0b100, Flags::[C] }
print_test(BitFlags::from_bits(4).unwrap());

// BitFlags { 0b11111110, Flags::[B, C, D] }
print_test(!Test::A);

// BitFlags { 0b11, Flags::[A, B] }
let flag1 = Test::A | Test::B;
print_test(flag1);

// BitFlags { 0b1100, Flags::[C, D] }
let flag2 = Test::C | Test::D;
print_test(flag2);

// BitFlags { 0b1001, Flags::[A, D] }
let flag3 = Test::A | Test::D;
print_test(flag3);

// BitFlags { 0b0, Flags::[] }
print_test(flag1 & flag2);

// BitFlags { 0b1111, Flags::[A, B, C, D] }
print_test(flag1 | flag2);
let a_b = Test::A | Test::B; // BitFlags<Test>
let a_c = Test::A | Test::C;
let b_c_d = Test::C | Test::B | Test::D;

// false
println!("{}", flag1.intersects(flag2));
// BitFlags<Test>(0b11, [A, B])
println!("{:?}", a_b);

// true
println!("{}", flag1.intersects(flag3));
// BitFlags<Test>(0b1, [A])
println!("{:?}", a_b & a_c);

// true
println!("{}", flag1.contains(Test::A | Test::B));
// Iterate over the flags like a normal set!
assert_eq!(a_b.iter().collect::<Vec<_>>(), &[Test::A, Test::B]);

// false
println!("{}", flag1.contains(Test::A | Test::B | Test::C));
assert!(a_b.contains(Test::A));
assert!(b_c_d.contains(Test::B | Test::C));
assert!(!(b_c_d.contains(a_b)));

// true
println!("{}", flag1.contains(Test::A));
assert!(a_b.intersects(a_c));
assert!(!(a_b.intersects(Test::C | Test::D)));
}
```
5 changes: 4 additions & 1 deletion enumflags/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[package]
name = "enumflags"
version = "0.3.0"
version = "0.4.2"
authors = ["maik klein <maikklein@googlemail.com>"]
description = "Bitflags"
license = "MIT"
Expand All @@ -12,3 +12,6 @@ documentation = "https://docs.rs/enumflags"
[features]
default = []
nostd = []

[dev-dependencies]
enumflags_derive = { path = "../enumflags_derive" }
104 changes: 87 additions & 17 deletions enumflags/src/lib.rs
Original file line number Diff line number Diff line change
@@ -1,44 +1,109 @@
//! # Enum Flags
//! `enumflags` defines a `BitFlags<T>` type for representing a set of named boolean values
//! efficiently, where `T` is an enum with explicitly defined values. Semantically similar to a
//! `HashSet<EnumWithoutAssociatedData>`, but much more efficient.
//!
//! ## Example
//! ```
//! extern crate enumflags;
//! #[macro_use]
//! extern crate enumflags_derive;
//!
//! use enumflags::BitFlags;
//!
//! #[derive(EnumFlags, Copy, Clone, Debug, PartialEq)]
//! #[repr(u8)]
//! enum Test {
//! A = 0b0001,
//! B = 0b0010,
//! C = 0b0100,
//! D = 0b1000,
//! }
//!
//! fn main() {
//! let a_b = Test::A | Test::B; // BitFlags<Test>
//! let a_c = Test::A | Test::C;
//! let b_c_d = Test::C | Test::B | Test::D;
//!
//! // BitFlags<Test>(0b11, [A, B])
//! println!("{:?}", a_b);
//!
//! // BitFlags<Test>(0b1, [A])
//! println!("{:?}", a_b & a_c);
//!
//! // Iterate over the flags like a normal set!
//! assert_eq!(a_b.iter().collect::<Vec<_>>(), &[Test::A, Test::B]);
//!
//! assert!(a_b.contains(Test::A));
//! assert!(b_c_d.contains(Test::B | Test::C));
//! assert!(!(b_c_d.contains(a_b)));
//!
//! assert!(a_b.intersects(a_c));
//! assert!(!(a_b.intersects(Test::C | Test::D)));
//! }
//! ```
#![warn(missing_docs)]
#![no_std]
#[cfg(not(feature = "nostd"))]
extern crate std;
#[cfg(feature = "nostd")]
extern crate core as std;

use std::ops::{BitAnd, BitOr, BitXor, Not};
use std::cmp::PartialOrd;
use std::fmt::{self, Formatter};
use std::iter::FromIterator;

pub trait BitFlagNum
: Default
+ BitOr<Self, Output = Self>
+ BitAnd<Self, Output = Self>
+ BitXor<Self, Output = Self>
+ Not<Output = Self>
+ PartialOrd<Self>
+ Copy
+ Clone {
/// Sealed trait
mod details {
use std::ops::{BitAnd, BitOr, BitXor, Not};
use std::cmp::PartialOrd;

pub trait BitFlagNum
: Default
+ BitOr<Self, Output = Self>
+ BitAnd<Self, Output = Self>
+ BitXor<Self, Output = Self>
+ Not<Output = Self>
+ PartialOrd<Self>
+ Copy
+ Clone {
}

impl BitFlagNum for u8 {}
impl BitFlagNum for u16 {}
impl BitFlagNum for u32 {}
impl BitFlagNum for u64 {}
impl BitFlagNum for usize {}
}

impl BitFlagNum for u8 {}
impl BitFlagNum for u16 {}
impl BitFlagNum for u32 {}
impl BitFlagNum for u64 {}
impl BitFlagNum for usize {}
use details::BitFlagNum;

/// A trait automatically implemented by `derive(EnumFlags)` on `T` to enable debug printing of
/// `BitFlags<T>`. This is necessary because the names of the variants are needed.
pub trait BitFlagsFmt
where
Self: RawBitFlags,
{
/// The implementation of Debug redirects here.
fn fmt(flags: BitFlags<Self>, f: &mut Formatter) -> fmt::Result;
}

/// A trait automatically implemented by `derive(EnumFlags)` to make the enum a valid type parameter
/// for BitFlags.
pub trait RawBitFlags: Copy + Clone {
/// The underlying integer type.
type Type: BitFlagNum;

/// Return a value with all flag bits set.
fn all() -> Self::Type;

/// Return the bits as a number type.
fn bits(self) -> Self::Type;

/// Return a slice that contains each variant exactly one.
fn flag_list() -> &'static [Self];
}

/// Represents a set of flags of some type `T`.
#[derive(Copy, Clone)]
pub struct BitFlags<T: RawBitFlags> {
val: T::Type,
Expand Down Expand Up @@ -78,7 +143,7 @@ where
unsafe { BitFlags::new(T::Type::default()) }
}

/// Sets all flags.
/// Create a BitFlags with all flags set.
pub fn all() -> Self {
unsafe { BitFlags::new(T::all()) }
}
Expand Down Expand Up @@ -142,6 +207,11 @@ where
pub fn remove<B: Into<BitFlags<T>>>(&mut self, other: B) {
*self = *self & !other.into();
}

/// Returns an iterator that yields each set flag
pub fn iter(self) -> impl Iterator<Item = T> {
T::flag_list().iter().cloned().filter(move |&flag| self.contains(flag))
}
}

impl<T, B> std::cmp::PartialEq<B> for BitFlags<T>
Expand Down
8 changes: 4 additions & 4 deletions enumflags_derive/Cargo.toml
Original file line number Diff line number Diff line change
@@ -1,16 +1,16 @@
[package]
name = "enumflags_derive"
version = "0.4.0"
version = "0.4.2"
authors = ["maik klein <maikklein@googlemail.com>"]
description = "Bitflags"
license = "MIT"
repository = "https://github.com/MaikKlein/enumflags"
keywords = ["enum", "bitflag", "flag", "bitflags"]

[dependencies]
syn = {version = "0.12.5", features = ["extra-traits"]}
quote = "0.4"
proc-macro2 = "0.2"
syn = {version = "^0.15", features = ["extra-traits"]}
quote = "^0.6"
proc-macro2 = "^0.4"

[lib]
proc-macro = true
Expand Down
Loading