bitflags_attr_macros/lib.rs
1use proc_macro::TokenStream;
2use quote::ToTokens;
3use syn::{Error, Result};
4use typed::{Args, Bitflag};
5
6mod typed;
7
8/// An attribute macro that transforms an C-like enum into a bitflag struct type implementing an
9/// ergonomic end-user API, similar to the `bitflags` crate, and implementing many helpful traits
10/// (listed in more details below).
11///
12/// The attribute requires that the [`Clone`] and [`Copy`] traits are derived for the type.
13///
14/// [`Clone`]: ::core::clone::Clone
15/// [`Copy`]: ::core::marker::Copy
16///
17/// ## Examples
18///
19/// Generate a flags type using `u8` as the bits type:
20/// ```rust
21/// # use bitflag_attr::bitflag;
22///
23/// #[bitflag(u8)]
24/// #[derive(Clone, Copy)]
25/// enum Flags {
26/// A = 1,
27/// B = 1 << 1,
28/// C = 0b0000_0100,
29/// }
30/// ```
31///
32/// Flags may refer to other flags using their names:
33///
34/// ```rust
35/// # use bitflag_attr::bitflag;
36///
37/// #[bitflag(u8)]
38/// #[derive(Clone, Copy)]
39/// enum Flags {
40/// A = 1,
41/// B = 1 << 1,
42/// C = 0b0000_0100,
43/// AB = A | B,
44/// }
45/// ```
46///
47/// Flags may also refer to other flags using their `bits` method value, like `bitflags` crate:
48///
49/// ```rust
50/// # use bitflag_attr::bitflag;
51///
52/// #[bitflag(u8)]
53/// #[derive(Clone, Copy)]
54/// enum Flags {
55/// A = 1,
56/// B = 1 << 1,
57/// C = 0b0000_0100,
58/// AB = Flags::A.bits() | Flags::B.bits(),
59/// }
60/// ```
61///
62/// It's possible to use more derives and attributes by simply adding them
63///
64/// ```rust
65/// # use core::fmt::Debug as ExternalDerive
66///
67/// #[bitflag(u8)]
68/// #[derive(Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash, ExternalDerive)]
69/// enum Flags {
70/// A = 1,
71/// B = 1 << 1,
72/// C = 0b0000_0100,
73/// AB = Flags::A.bits() | Flags::B.bits(),
74/// }
75/// ```
76///
77/// ## Known and unknown flags
78///
79/// The variant of the enum are flags. They will be expanded to type-associated constants. Every
80/// variant value is a known flag, while every not specified value is a unknown flag.
81///
82/// There are operation that will truncate out the unknown values. But tha can be configured if
83/// desired; more on that on [Externally defined flags](#externally-defined-flags)
84///
85/// ## Externally defined flags
86///
87/// If you're generating flags types from an external source, such as a C API, you can use the
88/// `#[non_exhaustive]` attribute to communicate to the bitflags macro that there may be more valid
89/// flags then the known flags.
90///
91/// Without extra configuration, it defaults to `!0` (all bits set) as a mask of all bits the
92/// external source may ever set, i.e. all bits are considered as possible values.
93///
94/// ```
95/// use bitflag_attr::bitflag;
96///
97/// #[bitflag(u32)]
98/// #[non_exhaustive] // All bits are considered as possible values.
99/// #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
100/// pub enum Flags {
101/// /// The value `A`, at bit position `0`.
102/// A = 0b00000001,
103/// /// The value `B`, at bit position `1`.
104/// B = 0b00000010,
105/// /// The value `C`, at bit position `2`.
106/// C = 0b00000100,
107///
108/// /// The combination of `A`, `B`, and `C`.
109/// ABC = A | B | C,
110/// }
111/// ```
112///
113/// But you can also configure it using the helper attribute `reserved_bits` with the value of
114/// valid bits that the external source may ever set.
115///
116/// ```
117/// use bitflag_attr::bitflag;
118///
119/// #[bitflag(u32)]
120/// #[non_exhaustive] // Communicate there is more potential valid flags than the known flags
121/// #[reserved_bits = 0b001001111] // Specify the reserved bits to take into consideration.
122/// #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
123/// pub enum Flags {
124/// /// The value `A`, at bit position `0`.
125/// A = 0b00000001,
126/// /// The value `B`, at bit position `1`.
127/// B = 0b00000010,
128/// /// The value `C`, at bit position `2`.
129/// C = 0b00000100,
130///
131/// /// The combination of `A`, `B`, and `C`.
132/// ABC = A | B | C,
133/// }
134/// ```
135///
136/// Why should you do this? Generated methods like `all` and truncating operators like `!` only
137/// consider bits in defined flags. Adding an unnamed flag makes those methods consider additional
138/// bits, without generating additional constants for them. It helps compatibility when the external
139/// source may start setting additional bits at any time.
140///
141/// ## Type representation
142///
143/// By default, the generated flag type will be `#[repr(transparent)]`, but you can explicit it on
144/// the definition as long is one of the supported ones (`C`, `Rust` and `transparent`):
145///
146/// ```rust
147/// # use bitflag_attr::bitflag;
148///
149/// #[repr(C)]
150/// #[bitflag(u8)]
151/// #[derive(Clone, Copy)]
152/// enum Flags {
153/// A = 1,
154/// B = 1 << 1,
155/// C = 1 << 2,
156/// }
157/// ```
158///
159/// ## Generated trait implementations
160///
161/// This macro generates some trait implementations: [`ops:Not`], [`ops:BitAnd`],
162/// [`ops:BitOr`], [`ops:BitXor`], [`ops:BitAndAssign`], [`ops:BitOrAssign`], [`ops:BitXorAssign`],
163/// [`fmt::Binary`], [`fmt::LowerHex`], [`fmt::UpperHex`], [`fmt::Octal`], [`From`], [`Extend`],
164/// [`FromIterator`], [`FromStr`] and [`IntoIterator`].
165///
166/// The custom [`fmt::Debug`] implementation will only be generated if it is included in the
167/// `#[derive(...)]` parameters.
168///
169/// The custom [`Default`] implementation will only be generated if it is included in the
170/// `#[derive(...)]` parameters.
171///
172/// ### Debug derive
173///
174/// The `bitflag` macro handles the [`fmt::Debug`] if specified in the derive list. When specified,
175/// a customized implementation is produced by the macro where it outputs human-readable, binary,
176/// octal and hexadecimal outputs of the flags value.
177///
178/// ```
179/// # use bitflag_attr::bitflag;
180///
181/// #[bitflag(u32)]
182/// #[derive(Debug, Clone, Copy)]
183/// pub enum Flags {
184/// A = 0b00000001,
185/// B = 0b00000010,
186/// C = 0b00000100,
187/// }
188/// ```
189///
190/// ### Default derive
191///
192/// The `bitflag` macro handles the [`Default`] if specified in the derive list. Without specifying
193/// a default variant, the default implementation is the same as a empty flag:
194///
195/// ```rust
196/// # use bitflag_attr::bitflag;
197///
198/// #[bitflag(u8)]
199/// #[derive(Clone, Copy, Default)] // Default is the same as `Flags::empty()`
200/// enum Flags {
201/// A = 1,
202/// B = 1 << 1,
203/// C = 1 << 2,
204/// }
205/// ```
206///
207/// But it can be specified like deriving [`Default`] on enum, using the `#[default]` helper attribute:
208///
209/// ```rust
210/// # use bitflag_attr::bitflag;
211///
212/// #[bitflag(u8)]
213/// #[derive(Clone, Copy, Default)]
214/// enum Flags {
215/// A = 1,
216/// B = 1 << 1,
217/// #[default] // `Flags::C` are the default value returned by `Default::default()`
218/// C = 1 << 2,
219/// }
220/// ```
221///
222/// ### Serde feature
223///
224/// If the crate is compiled with the `serde` feature, this crate will generate implementations for
225/// the `serde::{Serialize, Deserialize}` traits if they are included in the `#[derive(...)]`
226/// parameters.
227///
228/// ```no_run
229/// use bitflag_attr::bitflag;
230/// use serde::{Serialize, Deserialize};
231///
232/// #[bitflag(u32)]
233/// #[derive(Debug, Clone, Copy, Serialize, Deserialize)]
234/// pub enum Flags {
235/// /// The value `A`, at bit position `0`.
236/// A = 0b00000001,
237/// /// The value `B`, at bit position `1`.
238/// B = 0b00000010,
239/// /// The value `C`, at bit position `2`.
240/// C = 0b00000100,
241///
242/// /// The combination of `A`, `B`, and `C`.
243/// ABC = A | B | C,
244/// }
245/// ```
246///
247/// ### Arbitrary feature
248///
249/// If the crate is compiled with the `arbitrary` feature, this crate will generate implementations for
250/// the `arbitrary::Arbitrary` traits if they are included in the `#[derive(...)]`
251/// parameters, but it will not import/re-export these traits, your project must have `arbitrary` as
252/// a direct dependency.
253///
254/// ```no_run
255/// use bitflag_attr::bitflag;
256/// use arbitrary::Arbitrary;
257///
258/// #[bitflag(u32)]
259/// #[derive(Clone, Copy, Arbitrary)]
260/// enum Color {
261/// RED = 0x1,
262/// GREEN = 0x02,
263/// BLUE = 0x4,
264/// }
265/// ```
266///
267/// ### Bytemuck feature
268///
269/// If the crate is compiled with the `bytemuck` feature, this crate will generate implementations for
270/// the `bytemuck::{Pod, Zeroable}` traits if they are included in the `#[derive(...)]`
271/// parameters, but it will not import/re-export these traits, your project must have `bytemuck` as
272/// a direct dependency.
273///
274/// ```no_run
275/// use bitflag_attr::bitflag;
276/// use bytemuck::{Pod, Zeroable};
277///
278/// #[bitflag(u32)]
279/// #[derive(Debug, Clone, Copy, Pod, Zeroable)]
280/// pub enum Flags {
281/// /// The value `A`, at bit position `0`.
282/// A = 0b00000001,
283/// /// The value `B`, at bit position `1`.
284/// B = 0b00000010,
285/// /// The value `C`, at bit position `2`.
286/// C = 0b00000100,
287///
288/// /// The combination of `A`, `B`, and `C`.
289/// ABC = A | B | C,
290/// }
291/// ```
292///
293/// ### `const-mut-ref` feature
294///
295/// If the crate is compiled with the `const-mut-ref` feature, all type-associated API that takes
296/// `&mut self` will be generated as **const-fn**, meaning they can be used on `const` context.
297///
298/// **Note:** `&mut` on const function was stabilized on Rust 1.83.0, so using this feature flag on
299/// Rust versions below that will cause compilation errors
300///
301/// ### Custom types feature
302///
303/// If the crate is compiled with the `custom-types` feature, it allows to use more than the types
304/// defined in Rust `core` (`i8`,`u8`,`i16`,`u16`,`i32`,`u32`,`i64`,`u64`,`i128`,`u128`,`isize`,
305/// `usize`,`c_char`,`c_schar`,`c_uchar`,`c_short`,`c_ushort`,`c_int`,`c_uint`,`c_long`,`c_ulong`,
306/// `c_longlong`,`c_ulonglong`) and the unix types alias in the `libc` crate as long as it is a type
307/// alias to one of those types.
308///
309/// The reason it is behind a feature flag is that to ensure the validity of such constrain, we have
310/// to pay the price of having much worse error messages. With this feature enabled, a invalid type
311/// will cause a massive wall of error message.
312///
313///
314/// # More Examples
315///
316/// ```
317/// use bitflag_attr::bitflag;
318///
319/// #[bitflag(u32)]
320/// #[derive(Debug, Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
321/// pub enum Flags {
322/// /// The value `A`, at bit position `0`.
323/// A = 0b00000001,
324/// /// The value `B`, at bit position `1`.
325/// B = 0b00000010,
326/// /// The value `C`, at bit position `2`.
327/// C = 0b00000100,
328///
329/// /// The combination of `A`, `B`, and `C`.
330/// ABC = A | B | C,
331/// }
332/// ```
333///
334/// Without generating [`fmt::Debug`]:
335///
336/// ```
337/// use bitflag_attr::bitflag;
338///
339/// #[bitflag(u32)]
340/// #[derive(Clone, Copy, PartialEq, PartialOrd, Eq, Ord, Hash)]
341/// pub enum Flags {
342/// /// The value `A`, at bit position `0`.
343/// A = 0b00000001,
344/// /// The value `B`, at bit position `1`.
345/// B = 0b00000010,
346/// /// The value `C`, at bit position `2`.
347/// C = 0b00000100,
348///
349/// /// The combination of `A`, `B`, and `C`.
350/// ABC = A | B | C,
351/// }
352/// ```
353///
354/// # Syntax
355///
356/// ```rust,no_run
357/// #[bitflag($ty)]
358/// #[repr($repr_kind)] // optional: defaults to `repr(transparent)`
359/// #[non_exhaustive] // optional: If set, reserved_bits default to `!0`
360/// #[reserved_bits = $custom_extra_valid_expr] // optional
361/// #[derive(Clone, Copy, $other_derives)]
362/// $visibility enum $StructName {
363/// FlagOne = flag1_value_expr,
364/// FlagTwo = flag2_value_expr,
365/// // ...
366/// FlagN = flagn_value_expr,
367/// }
368/// ```
369///
370/// [`fmt::Debug`]: core::fmt::Debug
371/// [`ops:Not`]: core::ops::Not
372/// [`ops:BitAnd`]: core::ops::BitAnd
373/// [`ops:BitOr`]: core::ops::BitOr
374/// [`ops:BitXor`]: core::ops::BitXor
375/// [`ops:BitAndAssign`]: core::ops::BitAndAssign
376/// [`ops:BitOrAssign`]: core::ops::BitOrAssign
377/// [`ops:BitXorAssign`]: core::ops::BitXorAssign
378/// [`fmt::Binary`]: core::fmt::Binary
379/// [`fmt::LowerHex`]: core::fmt::LowerHex
380/// [`fmt::UpperHex`]: core::fmt::UpperHex
381/// [`fmt::Octal`]: core::fmt::Octal
382/// [`From`]: From
383/// [`FromStr`]: core::str::FromStr
384/// [`Default`]: core::default::Default
385/// [`IntoIterator`]: core::iter::IntoIterator
386/// [`Extend`]: core::iter::Extend
387/// [`FromIterator`]: core::iter::FromIterator
388#[proc_macro_attribute]
389pub fn bitflag(attr: TokenStream, item: TokenStream) -> TokenStream {
390 match bitflag_impl(attr, item) {
391 Ok(ts) => ts,
392 Err(err) => err.into_compile_error().into(),
393 }
394}
395
396fn bitflag_impl(attr: TokenStream, item: TokenStream) -> Result<TokenStream> {
397 let args: Args = syn::parse(attr)
398 .map_err(|err| Error::new(err.span(), "unexpected token: expected a `{integer}` type"))?;
399
400 let bitflag = Bitflag::parse(args, item)?;
401
402 Ok(bitflag.to_token_stream().into())
403}