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;