1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222
//! # E-Num(ber)
//!
//! Serialize enums into numbers.
//!
//! ## WARNING
//!
//! This library works with variant fields (e.g. `Variant1(u64)`) by
//! bitshifting the number representation of the contained value over
//! enough so that the tagging can fit on the right of the number. If
//! you're dealing with very large numbers in the fields or have a ton
//! of variants, data on the left side of the value will likely be lost.
//!
//! ## Basic Usage
//!
//! ```
//! #[macro_use]
//! extern crate e_num;
//!
//! use e_num::ENum;
//!
//! #[derive(ENum)]
//! enum A {
//! B,
//! C(u64),
//! }
//!
//! fn main() {
//! let b: usize = A::B.to_num();
//! println!("b as a number: {:#b}", b);
//! let b = A::from_num(b);
//! assert!(match b {
//! A::B => true,
//! _ => false,
//! });
//! let c = A::C(85).to_num();
//! println!("c as a number: {:#b}", c);
//! let c = A::from_num(c);
//! assert!(match c {
//! A::C(inner) => {
//! assert_eq!(inner, 85);
//! true
//! }
//! _ => false,
//! });
//! }
//! ```
//!
//! ## `start_at` and constant variants
//!
//! ```
//! #[macro_use]
//! extern crate e_num;
//!
//! use e_num::ENum;
//!
//! #[derive(ENum)]
//! // where the non-constant variants will start counting from
//! #[e_num(start_at = 9)]
//! enum A {
//! // pulls the specified variant out from the rest of them
//! // and matches it against that number. constant variants
//! // can't have a field.
//! #[e_num(constant = 2)]
//! B,
//! C,
//! D,
//! E,
//! }
//!
//! fn main() {
//! assert_eq!(A::B.to_num(), 2);
//! assert_eq!(A::C.to_num(), 9);
//! assert_eq!(A::D.to_num(), 10);
//! assert_eq!(A::E.to_num(), 11);
//! }
//! ```
#[allow(unused_imports)]
#[doc(hidden)]
#[macro_use]
extern crate e_num_derive;
#[doc(hidden)]
pub use e_num_derive::*;
#[cfg(test)]
mod tests {
use ENum;
#[derive(ENum)]
#[e_num(start_at = 0)]
enum Test1 {
A,
B(usize),
C,
}
#[test]
fn basic() {
assert!(match Test1::from_num(0) {
Test1::A => true,
_ => false,
});
}
#[test]
fn value_from_enum() {
assert!(match Test1::from_num(0b10101) {
Test1::B(v) => v == 0b101,
_ => false,
});
}
#[derive(ENum)]
enum Test2 {
#[e_num(constant = 9)]
A,
B,
}
#[test]
fn constant_variant() {
assert!(Test2::A.to_num() == 9);
}
}
pub trait ENum: Sized {
/// Parse a number into the type.
///
/// If you're `impl`ing `ENum` yourself, you don't *need* to
/// define this function, there is a default implementation
/// that uses the output from `try_from_num()`.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate e_num;
/// # use e_num::ENum;
/// # #[derive(ENum)]
/// # enum A {
/// # B,
/// # C,
/// # }
/// # let num = A::B.to_num();
/// let a = A::from_num(num);
/// assert!(match a {
/// A::B => true,
/// _ => false,
/// });
/// ```
///
/// # Panics
///
/// This function should panic if it cannot parse the number into
/// its type; e.g. you should only pass to this function the output
/// of `.to_num()`. If you want to handle a parsing error, use `try_from_num()`.
fn from_num(num: usize) -> Self {
Self::try_from_num(num).expect("Couldn't parse number into type")
}
/// The error-handling counterpart of `from_num()`.
///
/// Use this if you're not sure whether or not the number you're passing to the
/// function is valid.
///
/// # Examples
///
/// ```
/// # #![allow(unused_variables)]
/// # #[macro_use] extern crate e_num;
/// # use e_num::ENum;
/// # #[derive(ENum)]
/// # enum A {
/// # B,
/// # C,
/// # D,
/// # }
/// # let sketchy_number = 0b11;
/// if let Some(val) = A::try_from_num(sketchy_number) {
/// // handle val
/// } else {
/// // handle error
/// }
/// ```
fn try_from_num(num: usize) -> Option<Self>;
/// Convert self to a serializable number.
///
/// # Examples
///
/// ```
/// # #[macro_use] extern crate e_num;
/// # use e_num::ENum;
/// # #[derive(ENum)]
/// # enum A {
/// # B,
/// # C,
/// # }
/// let num = A::B.to_num();
/// // later...
/// let a = A::from_num(num);
/// assert!(match a {
/// A::B => true,
/// _ => false,
/// });
/// ```
fn to_num(&self) -> usize;
}
macro_rules! impl_e_num_num {
($($num:ty),*) => {
$(impl ENum for $num {
fn try_from_num(num:usize) -> Option<Self> {
Some(Self::from_num(num))
}
fn from_num(num: usize) -> Self {
num as Self
}
fn to_num(&self) -> usize {
*self as usize
}
})*
};
}
impl_e_num_num!(usize, u64, u32, u16);