c_enum/
lib.rs

1//! A macro for easily defining structs that act like C enums.
2//!
3//! The [`c_enum!`] macro generates structs that behave roughly like a C enum:
4//! they have a set of constants that have integer values but can be assigned
5//! integer values that don't correspond to any of the existing names.
6//!
7//! # Examples
8//! ```
9//! use c_enum::c_enum;
10//!
11//! c_enum! {
12//!     #[derive(Copy, Clone, PartialEq, Eq, Hash)]
13//!     pub enum MyEnum: u32 {
14//!         A,
15//!         B = 5,
16//!     }
17//! }
18//!
19//! let v1 = MyEnum::A;       // declared variant
20//! let v2 = MyEnum::from(3); // also supports variants that are not declared
21//!
22//! match v1 { // we can match if we derive PartialEq
23//!     MyEnum::A => println!("got an A"),
24//!     MyEnum::B => println!("got a B"),
25//!
26//!     // We still need to handle other variants
27//!     _ => println!("got another variant"),
28//! }
29//! ```
30//!
31//! # Visibility
32//! The `c_enum!` macro supports visibility, just like you would do for a normal
33//! rust enum.
34//!
35//! ```
36//! # #[macro_use]
37//! # extern crate c_enum;
38//! #
39//! mod example {
40//!     c_enum! {
41//!         pub enum Enum1: u8 {
42//!             A,
43//!         }
44//!     }
45//!
46//!     c_enum! {
47//! #       pub
48//!         enum Enum2: u8 {
49//!             B,
50//!         }
51//!     }
52//! }
53//!
54//! # fn main() {
55//! let val1 = example::Enum1::A;
56//! let val2 = example::Enum2::B; // error: struct `Enum2` is private
57//! # }
58//! ```
59//!
60//! # Attributes
61//! Attributes can be added to the generated type or variants as normal. Note
62//! that the variants are converted to constants so macros expecting an enum
63//! variant will not work.
64//!
65//! ## Applying Attributes to Generated `impl` Blocks
66//! Sometimes you need to apply attributes to the generated `impl` blocks (e.g.
67//! to silence warnings). `c_enum!` supports adding an extra `impl` block at the
68//! end and will apply those attributes to the generated `impl` block.
69//! ```
70//! #![deny(missing_docs)]
71//! # //! crate docs...
72//! # use c_enum::c_enum;
73//!
74//! c_enum! {
75//!     /// This crate requires that all items be documented.
76//!     ///
77//!     /// However, we don't want to document the members of this enum for one
78//!     /// reason or another.
79//!     pub enum SomeCEnum : u32 {
80//!         A = 0,
81//!         B
82//!     }
83//!
84//!     // So we attach the #[allow] directive to this extra impl block here
85//!     // and it will be added to the one generated by the macro.
86//!     #[allow(missing_docs)]
87//!     impl {}
88//! }
89//! ```
90//!
91//! # Representation
92//! It is valid to add a `#[repr(C)]` or `#[repr(transparent)]` attribute to the
93//! generated type. The generated type is guaranteed to be a newtype whose only
94//! member is the inner type.
95//!
96//! # Value Assignment
97//! By default, enum values are assigned like they would be for a C enum: the
98//! first variant is 0 and subsequent variants increase by 1 unless assigned a
99//! value.
100//!
101//! ```
102//! # #[macro_use]
103//! # extern crate c_enum;
104//! #
105//! c_enum! {
106//!     pub enum Enum: u32 {
107//!         A,     // value of 0
108//!         B,     // value of 1
109//!         C = 5, // value of 5
110//!         D,     // value of 6
111//!     }
112//! }
113//! # fn main() {}
114//! ```
115//!
116//! ## Non-String Inner Types
117//! It is also possible to define enum types whose inner value is not an
118//! integer.
119//!
120//! ```
121//! # #[macro_use]
122//! # extern crate c_enum;
123//! #
124//! c_enum! {
125//!     pub enum StringEnum: &'static str {
126//!         Hello = "Hello",
127//!         World = "World",
128//!     }
129//! }
130//! # fn main() {}
131//! ```
132//!
133//! Note that at this time generics are not supported so any inner value type
134//! must be both concrete and `'static`. Furthermore, you will need to assign a
135//! value to each variant of such an enum.
136//!
137//! # What's implemented by `c_enum!`
138//! The [`c_enum!`] macro implements some traits by default, but leaves the rest
139//! available for you to choose the semantics of the rest.
140//!
141//! ## Formatting
142//! - [`Debug`], but only if the inner type implements [`PartialEq`] and
143//!   [`Debug`].
144//!
145//! ## Conversion
146//! - [`From`] to convert from the inner type and vice versa.
147//!
148//! # Generated Code
149//! ```
150//! # #[macro_use]
151//! # extern crate c_enum;
152//! #
153//! c_enum! {
154//!     #[repr(transparent)]
155//!     #[derive(Copy, Clone, PartialEq, Eq, Hash)]
156//!     enum Enum: u32 {
157//!         A,
158//!         B = 5,
159//!     }
160//! }
161//! # fn main() {}
162//! ```
163//! is expanded into (roughly)
164//! ```
165//! # macro_rules! ignore { {$( $tt:tt )*} => {} }
166//! # #[macro_use]
167//! # extern crate c_enum;
168//! #
169//! #[repr(transparent)]
170//! #[derive(Copy, Clone, PartialEq, Eq, Hash)]
171//! struct Enum(pub u32);
172//!
173//! impl Enum {
174//!     pub const A: Self = Self(0);
175//!     pub const B: Self = Self(5);
176//! }
177//!
178//! # ignore! {
179//! impl core::fmt::Debug for Enum
180//! where
181//!     u32: core::cmp::PartialEq
182//! {
183//!     ...
184//! }
185//!
186//! // more trait impls...
187//! # }
188//! # fn main() {}
189//! ```
190//!
191//! # Motivation
192//! When writing bindings for C libraries which use enums there are a few
193//! options for declaring rust versions of C enums.
194//! - Use a rust enum.
195//! - Use a raw integer and a bunch of constants.
196//! - Use a newtype and a set of constants.
197//!
198//! All of them have use cases for which they are valid:
199//! - Rust enums work when calling from rust code into C code. The one caveat
200//!   here being that if the underlying C library adds a new variant but the
201//!   rust wrapper does not then users of the rust library are stuck. Another
202//!   case that is valid is if it is known that no new variants will be added to
203//!   the underlying C enum and the library is ok with either UB or doing
204//!   conversions at the API boundary.
205//! - Raw integers and constants is useful for autogenerated bindings that want
206//!   to exactly match the layout of the C headers.
207//! - A newtype + constants is suitable for the remaining cases. It still
208//!   behaves similar to a rust enum but matches the actual semantics of C
209//!   enums. It also continues to work if the C library adds new variants and
210//!   the rust wrapper is not updated.
211//!
212//! This crate is a generator for the third option.
213//!
214//! [`Debug`]: core::fmt::Debug
215//! [`PartialEq`]: core::cmp::PartialEq
216
217#![no_std]
218#![cfg_attr(docsrs, feature(doc_cfg))]
219
220extern crate self as c_enum;
221
222#[cfg(doc)]
223#[doc = include_str!("../README.md")]
224mod readme {}
225
226mod decl_variants;
227
228#[doc(hidden)]
229/// A trait that is automatically implemented for all C enums.
230pub trait CEnum: From<Self::Inner> + Into<Self::Inner> {
231    /// The inner type of this enum.
232    type Inner;
233
234    /// Get the string name corresponding to the current value, if there is one.
235    fn variant_label(&self) -> Option<&'static str>
236    where
237        Self::Inner: PartialEq;
238}
239
240/// The macro used to generate the C enum structure.
241///
242/// See the [crate level docs](crate) for complete documentation.
243#[macro_export]
244macro_rules! c_enum {
245    {
246        $( #[$attr:meta] )*
247        $vis:vis enum $name:ident : $inner:ty {
248            $(
249                $( #[ $field_attr:meta ] )*
250                $field:ident $( = $value:expr )?
251            ),* $(,)?
252        }
253
254        $(
255            $( #[$iattr:meta] )*
256            impl {}
257        )?
258    } => {
259        $crate::__c_enum_no_debug! {
260            $( #[$attr] )*
261            $vis enum $name : $inner {
262                $(
263                    $( #[ $field_attr ] )*
264                    $field $( = $value )?
265                ),*
266            }
267
268            $(
269                $( #[$iattr] )*
270                impl {}
271            )?
272        }
273
274        impl ::core::fmt::Debug for $name
275        where
276            $inner: ::core::fmt::Debug,
277            $inner: ::core::cmp::PartialEq
278        {
279            fn fmt(
280                &self,
281                f: &mut ::core::fmt::Formatter<'_>
282            ) -> ::core::fmt::Result {
283                use $crate::CEnum;
284
285                match self.variant_label() {
286                    Some(variant) => {
287                        f.write_fmt(::core::format_args!(
288                            "{}::{}", ::core::stringify!($name), variant
289                        ))
290                    },
291                    None => f
292                        .debug_tuple(::core::stringify!($name))
293                        .field(&self.0)
294                        .finish()
295                }
296            }
297        }
298    };
299    // Catch cases where there are multiple enums declared in the same block.
300    //
301    // This was valid up until version 0.2.0 so providing a good error message
302    // is useful.
303    {
304        $( #[$attr:meta] )*
305        $vis:vis enum $name:ident : $inner:ty {
306            $(
307                $( #[ $field_attr:meta ] )*
308                $field:ident $( = $value:expr )?
309            ),* $(,)?
310        }
311
312        $( $error:tt )+
313    } => {
314        $crate::c_enum! {
315            $( #[$attr] )*
316            $vis enum $name : $inner {
317                $(
318                    $( #[$field_attr] )*
319                    $field $( = $value )?,
320                )*
321            }
322        }
323
324        $crate::__c_enum_expects_impl_or_nothing! { $( $error )+ }
325
326        compile_error!(
327            "Declaring multiple enums using a single c_enum! macro block is no longer supported",
328        );
329    }
330}
331
332// TODO: not sure if this is worth adding to the public API.
333/// The macro used to generate the C enum structure.
334///
335/// This version does not generate a [`Debug`] impl.
336///
337/// See the [crate level docs](crate) for complete documentation.
338///
339/// [`Debug`]: core::fmt::Debug
340#[macro_export]
341#[doc(hidden)]
342macro_rules! __c_enum_no_debug {
343    {
344        $( #[$attr:meta] )*
345        $vis:vis enum $name:ident : $inner:ty {
346            $(
347                $( #[ $field_attr:meta ] )*
348                $field:ident $( = $value:expr )?
349            ),* $(,)?
350        }
351
352        $(
353            $( #[$iattr:meta] )*
354            impl {}
355        )?
356    } => {
357        $( #[$attr] )*
358        $vis struct $name(pub $inner);
359
360        #[allow(non_upper_case_globals)]
361        $( $( #[$iattr] )* )?
362        impl $name {
363            $crate::__c_enum_decl_variants!(
364                impl($name, $inner, 0)
365                $(
366                    $( #[$field_attr] )*
367                    $field $( = $value )?,
368                )*
369            );
370        }
371
372        #[automatically_derived]
373        impl From<$inner> for $name {
374            fn from(value: $inner) -> Self {
375                Self(value)
376            }
377        }
378
379        #[automatically_derived]
380        impl From<$name> for $inner {
381            fn from(value: $name) -> Self {
382                value.0
383            }
384        }
385
386        #[automatically_derived]
387        impl $crate::CEnum for $name {
388            type Inner = $inner;
389
390            fn variant_label(&self) -> Option<&'static str>
391            where
392                Self::Inner: PartialEq
393            {
394                Some(match &self.0 {
395                    $( value if Self::$field.0 == *value => ::core::stringify!($field), )*
396                    _ => return None,
397                })
398            }
399        }
400    };
401}
402
403/// Helper macro to emit a "no rules expected the token `...`" error message.
404///
405/// The input spec here matches the one in the `c_enum!` macro after the end of
406/// the enum declaration. That way we give the appropriate error message in case
407/// it's due to a typo and not multiple declarations.
408#[doc(hidden)]
409#[macro_export]
410macro_rules! __c_enum_expects_impl_or_nothing {
411    {
412        $(
413            $( #[$iattr:meta] )*
414            impl {}
415        )?
416    } => {};
417}
418
419/// Helper macro for defining stuff in c_enum.
420///
421/// These could be a bunch of different macros but those would clutter up the
422/// import namespace when using something like rust-analyzer. By using a single
423/// internal macro we can avoid that.
424#[doc(hidden)]
425#[macro_export]
426macro_rules! __c_enum_impl {
427    (impl(first_expr) $first:expr $( , $rest:expr )*) => {
428        $first
429    }
430}
431
432// This needs to be after all the macro definitions.
433/// This module shows an example of code generated by the macro.
434///
435/// The source code of this module is
436/// ```
437#[doc = include_str!("example.rs")]
438/// ```
439#[cfg(doc)]
440#[cfg_attr(docsrs, doc(cfg(doc)))]
441pub mod example;