Skip to content

Commit

Permalink
Merge pull request #351 from KodrAus/feat/public-traits
Browse files Browse the repository at this point in the history
Support ejecting flags types from the bitflags macro
  • Loading branch information
KodrAus committed May 17, 2023
2 parents 1d8388b + 796946d commit c947503
Show file tree
Hide file tree
Showing 67 changed files with 3,306 additions and 1,439 deletions.
38 changes: 16 additions & 22 deletions .github/workflows/rust.yml
Original file line number Diff line number Diff line change
Expand Up @@ -10,40 +10,21 @@ permissions:

jobs:
check:
name: "Tests / OS: ${{ matrix.os }} - ${{ matrix.channel }}-${{ matrix.rust_target }}"
runs-on: ${{ matrix.os }}
name: "Tests"
runs-on: ubuntu-latest
strategy:
matrix:
exclude:
- os: macos-10.15
rust_target: x86_64-gnu
- os: macos-10.15
rust_target: x86_64-msvc
- os: windows-2019
rust_target: x86_64-apple-darwin
- os: ubuntu-20.04
rust_target: x86_64-msvc
- os: ubuntu-20.04
rust_target: x86_64-apple-darwin
channel:
- stable
- beta
- nightly
os:
- macos-10.15
- windows-2019
- ubuntu-20.04
rust_target:
- x86_64-gnu
- x86_64-msvc
- x86_64-apple-darwin

steps:
- name: Checkout repository
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab

- name: Install Rust Toolchain
run: rustup default ${{ matrix.channel }}-${{ matrix.rust_target }}
run: rustup default ${{ matrix.channel }}

- name: Install cargo-hack
run: cargo install cargo-hack
Expand Down Expand Up @@ -87,6 +68,19 @@ jobs:
cd ./tests/smoke-test
cargo +$msrv build
mips:
name: Tests / MIPS (Big Endian)
runs-on: ubuntu-latest
steps:
- name: Checkout sources
uses: actions/checkout@8e5e7e5ab8b370d6c329ec480221332ada57f0ab

- name: Install Cross
run: cargo install cross

- name: Default features
run: cross test --target mips-unknown-linux-gnu

embedded:
name: Build (embedded)
runs-on: ubuntu-latest
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ rustversion = "1.0"
serde_derive = "1.0"
serde_json = "1.0"
serde_test = "1.0"
zerocopy = "0.6"
arbitrary = { version = "1.0", features = ["derive"] }
bytemuck = { version = "1.0", features = ["derive"] }

Expand Down
85 changes: 85 additions & 0 deletions examples/custom_bits_type.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
use std::ops::{BitAnd, BitOr, BitXor, Not};

use bitflags::{Flags, Flag, Bits};

// Define a custom container that can be used in flags types
// Note custom bits types can't be used in `bitflags!`
// without making the trait impls `const`. This is currently
// unstable
#[derive(Clone, Copy, Debug)]
pub struct CustomBits([bool; 3]);

impl Bits for CustomBits {
const EMPTY: Self = CustomBits([false; 3]);

const ALL: Self = CustomBits([true; 3]);
}

impl PartialEq for CustomBits {
fn eq(&self, other: &Self) -> bool {
self.0 == other.0
}
}

impl BitAnd for CustomBits {
type Output = Self;

fn bitand(self, other: Self) -> Self {
CustomBits([self.0[0] & other.0[0], self.0[1] & other.0[1], self.0[2] & other.0[2]])
}
}

impl BitOr for CustomBits {
type Output = Self;

fn bitor(self, other: Self) -> Self {
CustomBits([self.0[0] | other.0[0], self.0[1] | other.0[1], self.0[2] | other.0[2]])
}
}

impl BitXor for CustomBits {
type Output = Self;

fn bitxor(self, other: Self) -> Self {
CustomBits([self.0[0] & other.0[0], self.0[1] & other.0[1], self.0[2] & other.0[2]])
}
}

impl Not for CustomBits {
type Output = Self;

fn not(self) -> Self {
CustomBits([!self.0[0], !self.0[1], !self.0[2]])
}
}

#[derive(Clone, Copy, Debug)]
pub struct CustomFlags(CustomBits);

impl CustomFlags {
pub const A: Self = CustomFlags(CustomBits([true, false, false]));
pub const B: Self = CustomFlags(CustomBits([false, true, false]));
pub const C: Self = CustomFlags(CustomBits([false, false, true]));
}

impl Flags for CustomFlags {
const FLAGS: &'static [Flag<Self>] = &[
Flag::new("A", Self::A),
Flag::new("B", Self::B),
Flag::new("C", Self::C),
];

type Bits = CustomBits;

fn bits(&self) -> Self::Bits {
self.0
}

fn from_bits_retain(bits: Self::Bits) -> Self {
CustomFlags(bits)
}
}

fn main() {
println!("{:?}", CustomFlags::A.union(CustomFlags::C));
}
23 changes: 23 additions & 0 deletions examples/custom_derive.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
//! An example of implementing the `BitFlags` trait manually for a flags type.

use std::str;

use bitflags::bitflags;

// Define a flags type outside of the `bitflags` macro as a newtype
// It can accept custom derives for libaries `bitflags` doesn't support natively
#[derive(zerocopy::AsBytes, zerocopy::FromBytes)]
#[repr(transparent)]
pub struct ManualFlags(u32);

// Next: use `impl Flags` instead of `struct Flags`
bitflags! {
impl ManualFlags: u32 {
const A = 0b00000001;
const B = 0b00000010;
const C = 0b00000100;
const ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
}
}

fn main() {}
50 changes: 25 additions & 25 deletions examples/fmt.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,40 +2,40 @@

use core::{fmt, str};

fn main() -> Result<(), bitflags::parser::ParseError> {
bitflags::bitflags! {
// You can `#[derive]` the `Debug` trait, but implementing it manually
// can produce output like `A | B` instead of `Flags(A | B)`.
// #[derive(Debug)]
#[derive(PartialEq, Eq)]
pub struct Flags: u32 {
const A = 1;
const B = 2;
const C = 4;
const D = 8;
}
bitflags::bitflags! {
// You can `#[derive]` the `Debug` trait, but implementing it manually
// can produce output like `A | B` instead of `Flags(A | B)`.
// #[derive(Debug)]
#[derive(PartialEq, Eq)]
pub struct Flags: u32 {
const A = 1;
const B = 2;
const C = 4;
const D = 8;
}
}

impl fmt::Debug for Flags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Debug::fmt(&self.0, f)
}
impl fmt::Debug for Flags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
bitflags::parser::to_writer(self, f)
}
}

impl fmt::Display for Flags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
fmt::Display::fmt(&self.0, f)
}
impl fmt::Display for Flags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
bitflags::parser::to_writer(self, f)
}
}

impl str::FromStr for Flags {
type Err = bitflags::parser::ParseError;
impl str::FromStr for Flags {
type Err = bitflags::parser::ParseError;

fn from_str(flags: &str) -> Result<Self, Self::Err> {
Ok(Self(flags.parse()?))
}
fn from_str(flags: &str) -> Result<Self, Self::Err> {
bitflags::parser::from_str(flags)
}
}

fn main() -> Result<(), bitflags::parser::ParseError> {
let flags = Flags::A | Flags::B;

println!("{}", flags);
Expand Down
58 changes: 58 additions & 0 deletions examples/macro_free.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
//! An example of implementing the `BitFlags` trait manually for a flags type.
//!
//! This example doesn't use any macros.

use std::{fmt, str};

use bitflags::{Flags, Flag};

// First: Define your flags type. It just needs to be `Sized + 'static`.
pub struct ManualFlags(u32);

// Not required: Define some constants for valid flags
impl ManualFlags {
pub const A: ManualFlags = ManualFlags(0b00000001);
pub const B: ManualFlags = ManualFlags(0b00000010);
pub const C: ManualFlags = ManualFlags(0b00000100);
pub const ABC: ManualFlags = ManualFlags(0b00000111);
}

// Next: Implement the `BitFlags` trait, specifying your set of valid flags
// and iterators
impl Flags for ManualFlags {
const FLAGS: &'static [Flag<Self>] = &[
Flag::new("A", Self::A),
Flag::new("B", Self::B),
Flag::new("C", Self::C),
];

type Bits = u32;

fn bits(&self) -> u32 {
self.0
}

fn from_bits_retain(bits: u32) -> Self {
Self(bits)
}
}

// Not required: Add parsing support
impl str::FromStr for ManualFlags {
type Err = bitflags::parser::ParseError;

fn from_str(input: &str) -> Result<Self, Self::Err> {
bitflags::parser::from_str(input)
}
}

// Not required: Add formatting support
impl fmt::Display for ManualFlags {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
bitflags::parser::to_writer(self, f)
}
}

fn main() {
println!("{}", ManualFlags::A.union(ManualFlags::B).union(ManualFlags::C));
}
35 changes: 23 additions & 12 deletions src/example_generated.rs
Original file line number Diff line number Diff line change
Expand Up @@ -9,30 +9,41 @@ __declare_public_bitflags! {
/// This is the same `Flags` struct defined in the [crate level example](../index.html#example).
/// Note that this struct is just for documentation purposes only, it must not be used outside
/// this crate.
pub struct Flags;
pub struct Flags
}

__declare_internal_bitflags! {
pub struct Field0: u32;
pub struct Iter;
pub struct IterRaw;
pub struct Field0: u32
}

__impl_internal_bitflags! {
Field0: u32, Flags, Iter, IterRaw {
A;
B;
C;
ABC;
Field0: u32, Flags {
// Field `A`.
///
/// This flag has the value `0b00000001`.
A = 0b00000001;
/// Field `B`.
///
/// This flag has the value `0b00000010`.
B = 0b00000010;
/// Field `C`.
///
/// This flag has the value `0b00000100`.
C = 0b00000100;
ABC = Self::A.bits() | Self::B.bits() | Self::C.bits();
}
}

__impl_public_bitflags! {
Flags: u32, Field0, Iter, IterRaw;
__impl_public_bitflags_forward! {
Flags: u32, Field0
}

__impl_public_bitflags_iter! {
Flags
}

__impl_public_bitflags_consts! {
Flags {
Flags: u32 {
/// Field `A`.
///
/// This flag has the value `0b00000001`.
Expand Down

0 comments on commit c947503

Please sign in to comment.