discrimin_ant/
lib.rs

1//! # Discrimin-Ant
2//!
3//! Discrimin-Ant is a tiny library to make working with `enum` discriminants just a little easier.
4//!
5//! ---
6//!
7//! Discrimin-Ant was originally built to make it easier to marshal enums for use for
8//! software systems that do not or cannot use the Rust ecosystem.
9//! It is intended to be used with [Serde](https://crates.io/crates/serde) to
10//! marshal and send enums over a network or save them to a file.
11//! This can be done by implementing a custom `serde::Serialize` and
12//! `serde::Deserialize` that treats an enum as a struct or tuple instead of a
13//! variant, since the variant serialize methods only provide the index of the enum
14//! instead of its discriminant. This allows enums to be serialized in a way such
15//! that rearranging the order of enums does not change the discriminant serialized.
16//!
17//! The primary features of this crate are the `Discriminantable` trait and the
18//! `discriminant` attribute.
19//! The `discriminant` attribute is meant to take the place of the `repr`
20//! attribute on enums, and will generate a few blocks of code:
21//! - An `impl` for `Discriminantable` for the enum, using the enum's actual discriminants
22//! - A fieldless version of the enum (regardless of whether or not the enum is already fieldless)
23//! - An `impl` for `Discriminantable` on the fieldless version of the enum
24//! - Other utilities on the fieldless version to ease use
25//!
26//! Specifically, given the enum
27//! ```no_run
28//! use discrimin_ant_proc::discriminant;
29//! #[discriminant(u8)]
30//! pub enum ComplexUEnum {
31//!     One(i32) = 1,
32//!     Two(i32),
33//!     Five { x: u32 } = 5,
34//!     Six { x: u32 },
35//! }
36//! ```
37//! the result will be
38//!
39//! ```no_run
40//! #[repr(u8)]
41//! pub enum ComplexUEnum {
42//!     One(i32) = 1,
43//!     Two(i32),
44//!     Five { x: u32 } = 5,
45//!     Six { x: u32 },
46//! }
47//! impl ComplexUEnum {
48//!     #[doc = r" Returns the discriminant of [Self]."]
49//!     pub const fn discriminant(&self) -> u8 {
50//!         unsafe { *core::ptr::from_ref::<Self>(self).cast::<u8>() }
51//!     }
52//! }
53//! impl discrimin_ant::Discriminantable for ComplexUEnum {
54//!     type Discriminant = u8;
55//!
56//!     fn discriminant(&self) -> Self::Discriminant {
57//!         self.discriminant()
58//!     }
59//! }
60//! #[doc = "Fieldless representations of [ComplexUEnum]. Used to extract discriminants without fully constructing the enum."]
61//! #[repr(u8)]
62//! pub enum ComplexUEnum_ {
63//!     #[doc = "A fieldless version of [ComplexUEnum::One], used to extract the variant's discriminant without needing to fully construct it."]
64//!     One = 1,
65//!     #[doc = "A fieldless version of [ComplexUEnum::Two], used to extract the variant's discriminant without needing to fully construct it."]
66//!     Two = 1 + 1u8,
67//!     #[doc = "A fieldless version of [ComplexUEnum::Five], used to extract the variant's discriminant without needing to fully construct it."]
68//!     Five = 5,
69//!     #[doc = "A fieldless version of [ComplexUEnum::Six], used to extract the variant's discriminant without needing to fully construct it."]
70//!     Six = 5 + 1u8,
71//! }
72//! impl ComplexUEnum_ {
73//!     #[doc = r" Returns the discriminant of [Self]."]
74//!     pub const fn discriminant(&self) -> u8 {
75//!         unsafe { *core::ptr::from_ref::<Self>(self).cast::<u8>() }
76//!     }
77//! }
78//! impl discrimin_ant::Discriminantable for ComplexUEnum_ {
79//!     type Discriminant = u8;
80//!
81//!     fn discriminant(&self) -> Self::Discriminant {
82//!         self.discriminant()
83//!     }
84//! }
85//! impl TryFrom<u8> for ComplexUEnum_ {
86//!     type Error = ();
87//!     fn try_from(value: u8) -> Result<Self, Self::Error> {
88//!         if value == (1) {
89//!             return Ok(Self::One);
90//!         }
91//!         if value == (1 + 1u8) {
92//!             return Ok(Self::Two);
93//!         }
94//!         if value == (5) {
95//!             return Ok(Self::Five);
96//!         }
97//!         if value == (5 + 1u8) {
98//!             return Ok(Self::Six);
99//!         }
100//!         Err(())
101//!     }
102//! }
103//! impl From<&ComplexUEnum> for ComplexUEnum_ {
104//!     fn from(value: &ComplexUEnum) -> Self {
105//!         match value {
106//!             ComplexUEnum::One(..) => Self::One,
107//!             ComplexUEnum::Two(..) => Self::Two,
108//!             ComplexUEnum::Five { .. } => Self::Five,
109//!             ComplexUEnum::Six { .. } => Self::Six,
110//!         }
111//!     }
112//! }
113//! ```
114//!
115//! Note that the `discriminant` attribute only supports [primitive representations](https://doc.rust-lang.org/reference/type-layout.html#primitive-representations)
116//! since those are the only types that have a [reliably accessible discriminant](https://doc.rust-lang.org/reference/items/enumerations.html#pointer-casting).
117//! Nevertheless, manual implementations of `Discriminant` can be made for any enum (and technically and other object).
118#![no_std]
119
120/// An enum with an accessible discriminant.
121pub trait Discriminantable {
122    /// The type of the discriminant.
123    type Discriminant: num_traits::PrimInt;
124
125    /// Returns the discriminant of the enum.
126    fn discriminant(&self) -> Self::Discriminant;
127}