flags_macro/lib.rs
1//! This crate provides a convenient macro [`flags`] for constructing bitflags.
2//! It's designed to be compatible with [`bitflags`] and [`enumflags`] but works
3//! with any bitflags-like types.
4//!
5//! [`bitflags`]: https://crates.io/crates/bitflags
6//! [`enumflags`]: https://crates.io/crates/enumflags
7//!
8//! # Examples
9//!
10//! `bitflags`:
11//!
12//! # #[macro_use]
13//! # extern crate flags_macro;
14//! #[macro_use]
15//! extern crate bitflags;
16//! # fn main() {
17//! bitflags! {
18//! struct Test: u32 {
19//! const A = 0b0001;
20//! const B = 0b0010;
21//! }
22//! }
23//!
24//! let flags0 = flags![Test::{}];
25//! let flags1 = flags![Test::{A}];
26//! let flags2 = flags![Test::{A | B}];
27//!
28//! assert_eq!(flags0, Test::empty());
29//! assert_eq!(flags1, Test::A);
30//! assert_eq!(flags2, Test::A | Test::B);
31//! # }
32//!
33//! `enumflags`:
34//!
35//! # #[macro_use]
36//! # extern crate flags_macro;
37//! #[macro_use]
38//! extern crate enumflags;
39//! # #[macro_use]
40//! # extern crate enumflags_derive;
41//! # fn main() {
42//! #[derive(EnumFlags, Copy, Clone, PartialEq, Eq, Debug)]
43//! #[repr(u8)]
44//! pub enum Test { A = 0b0001, B = 0b0010 }
45//!
46//! let flags0 = flags![Test::{}];
47//! let flags1 = flags![Test::{A}];
48//! let flags2 = flags![Test::{A | B}];
49//!
50//! assert_eq!(flags0, enumflags::BitFlags::empty());
51//! assert_eq!(flags1, Test::A);
52//! assert_eq!(flags2, Test::A | Test::B);
53//! # }
54//!
55#![no_std]
56use core::{iter::FromIterator, ops::BitOr};
57
58/// Emits an expression of type `<E as DefaultSet>::Set` given zero or more
59/// values of type `E` defined as associated constants or enumerate items of
60/// `E`.
61///
62/// # Examples
63///
64/// See the [module-level documentation].
65///
66/// [module-level documentation]: index.html
67///
68/// # Syntax
69///
70/// ```text
71/// flags![path::ty::{Item1 | ... | ItemN}]
72/// flags![path::ty::{Item1, ..., ItemN}]
73/// ```
74///
75/// `Item1` ... `ItemN` are identifiers. Conceptually, these expressions are
76/// expanded into:
77///
78/// ```text
79/// <path::ty as DefaultSet>::Set::from_iter([
80/// path::ty::Item1, ..., path::ty::ItemN
81/// ].iter().cloned())
82/// ```
83///
84/// Usually, this is equivalent to:
85///
86/// ```text
87/// path::ty::Item1 | ... | path::ty::ItemN
88/// ```
89///
90/// # Invalid usages
91///
92/// The path prefix (denoted as `path::ty::` in Section "Syntax") must not be
93/// empty.
94///
95#[macro_export(local_inner_macros)]
96macro_rules! flags {
97 ( $($ns:ident::)* {$($items:tt)*} ) => (
98 <__containing_type!($($ns::)*) as $crate::DefaultSet>
99 ::set_from_iter(set_array![$($ns::)*{$($items)*}].iter().cloned())
100 )
101}
102
103/// Gets `A::B` from `A::B::`.
104#[doc(hidden)]
105#[macro_export(local_inner_macros)]
106macro_rules! __containing_type {
107 () => {
108 compile_error!("The path prefix (`A::` of `flags![A::{...}]`) must not be empty.")
109 };
110 ($ns:ident::) => {$ns};
111 ($ns:ident::$($rest:ident::)*) => {$ns$(::$rest)*}
112}
113
114/// Emits an array expression containing zero or more values defined within
115/// the same namespace (or a similar language construct).
116///
117/// # Syntax
118///
119/// ```text
120/// set_array![path1::path2::{Item1 | ... | ItemN}]
121/// set_array![path1::path2::{Item1, ..., ItemN}]
122/// ```
123///
124/// `Item1` ... `ItemN` are identifiers. These expressions are expanded into:
125///
126/// ```text
127/// [path1::path2::Item1, ..., path1::path2::ItemN]
128/// ```
129///
130/// # Examples
131///
132/// # #[macro_use]
133/// # extern crate flags_macro;
134/// # fn main() {
135/// mod values {
136/// pub const A: u32 = 1;
137/// pub const B: u32 = 2;
138/// pub const C: u32 = 3;
139/// }
140///
141/// let array0: [u32; 0] = set_array![values::{}];
142/// let array1 = set_array![values::{A}];
143/// let array2a = set_array![values::{A | B}];
144/// let array2b = set_array![values::{A, B}]; // alternative syntax
145///
146/// assert_eq!(array0, []);
147/// assert_eq!(array1, [values::A]);
148/// assert_eq!(array2a, [values::A, values::B]);
149/// assert_eq!(array2b, [values::A, values::B]);
150/// # }
151#[macro_export(local_inner_macros)]
152macro_rules! set_array {
153 ( $($ns:ident::)* {$($items:tt)*} ) => (
154 __set_array![@[] $($ns::)*{$($items)*}]
155 )
156}
157
158#[doc(hidden)]
159#[macro_export(local_inner_macros)]
160macro_rules! __set_array {
161 ( @[$($out:tt)*] $($ns:ident::)* {} ) => (
162 [$($out)*]
163 );
164
165 ( @[$($out:tt)*] $($ns:ident::)* {$tail:ident} ) => (
166 [$($out)* $($ns::)*$tail]
167 );
168
169 ( @[$($out:tt)*] $($ns:ident::)* {$head:ident | $($rest:tt)*} ) => (
170 __set_array![
171 @[$($out)* $($ns::)*$head,]
172 $($ns::)*{$($rest)*}
173 ]
174 );
175
176 ( @[$($out:tt)*] $($ns:ident::)* {$head:ident , $($rest:tt)*} ) => (
177 __set_array![
178 @[$($out)* $($ns::)*$head,]
179 $($ns::)*{$($rest)*}
180 ]
181 )
182}
183
184/// A trait for getting the default "set" type from an "element" type.
185///
186/// This trait has a blanket implementation for bitflags-like types.
187pub trait DefaultSet: Sized {
188 type Set: FromIterator<Self>;
189
190 /// Construct a `Set` using `Set::from_iter`.
191 fn set_from_iter(iter: impl IntoIterator<Item = Self>) -> Self::Set {
192 Self::Set::from_iter(iter)
193 }
194}
195
196impl<T> DefaultSet for T
197where
198 T: BitOr,
199 <T as BitOr>::Output: FromIterator<Self>,
200{
201 type Set = <T as BitOr>::Output;
202}
203
204#[cfg(test)]
205mod tests {
206 #[test]
207 fn trailing_separators() {
208 mod values {
209 pub const A: u32 = 1;
210 pub const B: u32 = 2;
211 }
212
213 assert_eq!(set_array![values::{A | B |}], [values::A, values::B]);
214 assert_eq!(set_array![values::{A, B,}], [values::A, values::B]);
215 }
216}