discrimin_ant_proc/lib.rs
1//! # Discrimin-Ant Proc
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).
118use proc_macro::TokenStream;
119
120mod discriminant;
121
122/// Assign a `repr` and produce a `discriminant` method. Also produce a fieldless version of the
123/// enum to allow for easier access to the discriminants without needing to construct one.
124///
125/// * `repr`: The representation for the enum. Should be one of the standard `u*` or `i*` data types.
126/// See <https://doc.rust-lang.org/nomicon/other-reprs.html#repru-repri>
127/// and <https://doc.rust-lang.org/reference/type-layout.html#primitive-representations>
128///
129/// # Example
130/// Fieldless
131/// ```rust
132/// use discrimin_ant_proc::discriminant;
133/// #[discriminant(i8)]
134/// enum FieldlessEnum {
135/// NOne = -1,
136/// One = 1,
137/// Two = 2,
138/// Three,
139/// Five = 5,
140/// Seven = 7,
141/// Nine = 9,
142/// }
143///
144/// assert_eq!(FieldlessEnum::NOne.discriminant(), -1);
145/// assert_eq!(FieldlessEnum::One.discriminant(), 1);
146/// assert_eq!(FieldlessEnum::Two.discriminant(), 2);
147/// assert_eq!(FieldlessEnum::Three.discriminant(), 3);
148/// assert_eq!(FieldlessEnum::Five.discriminant(), 5);
149/// assert_eq!(FieldlessEnum::Seven.discriminant(), 7);
150/// assert_eq!(FieldlessEnum::Nine.discriminant(), 9);
151/// // Still generates the Fieldless version for consistency
152/// assert_eq!(FieldlessEnum_::NOne.discriminant(), -1);
153/// assert_eq!(FieldlessEnum_::One.discriminant(), 1);
154/// assert_eq!(FieldlessEnum_::Two.discriminant(), 2);
155/// assert_eq!(FieldlessEnum_::Three.discriminant(), 3);
156/// assert_eq!(FieldlessEnum_::Five.discriminant(), 5);
157/// assert_eq!(FieldlessEnum_::Seven.discriminant(), 7);
158/// assert_eq!(FieldlessEnum_::Nine.discriminant(), 9);
159/// ```
160///
161/// With fields
162/// ```rust
163/// use discrimin_ant_proc::discriminant;
164/// #[discriminant(i8)]
165/// enum FieldEnum {
166/// NOne(u8) = -1,
167/// One { item: u8 } = 1,
168/// Two = 2,
169/// Three(u8, i16),
170/// Five = 5,
171/// Seven { item_a: i128, item_b: u32 } = 7,
172/// Nine = 9,
173/// }
174///
175/// assert_eq!(FieldEnum::NOne(0).discriminant(), -1);
176/// assert_eq!(FieldEnum::One { item: 0 }.discriminant(), 1);
177/// assert_eq!(FieldEnum::Two.discriminant(), 2);
178/// assert_eq!(FieldEnum::Three(0, 0).discriminant(), 3);
179/// assert_eq!(FieldEnum::Five.discriminant(), 5);
180/// assert_eq!(FieldEnum::Seven { item_a: 0, item_b: 0 }.discriminant(), 7);
181/// assert_eq!(FieldEnum::Nine.discriminant(), 9);
182/// // Fieldless version has the same discriminants
183/// assert_eq!(FieldEnum_::NOne.discriminant(), -1);
184/// assert_eq!(FieldEnum_::One.discriminant(), 1);
185/// assert_eq!(FieldEnum_::Two.discriminant(), 2);
186/// assert_eq!(FieldEnum_::Three.discriminant(), 3);
187/// assert_eq!(FieldEnum_::Five.discriminant(), 5);
188/// assert_eq!(FieldEnum_::Seven.discriminant(), 7);
189/// assert_eq!(FieldEnum_::Nine.discriminant(), 9);
190/// ```
191#[proc_macro_attribute]
192pub fn discriminant(attr: TokenStream, item: TokenStream) -> TokenStream {
193 let item = proc_macro2::TokenStream::from(item);
194 let attr = proc_macro2::TokenStream::from(attr);
195
196 discriminant::discriminant_impl(attr, item).into()
197}