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}