option_like/lib.rs
1//! Create your own enum type that behaves like Rust's `Option` but with custom names.
2//!
3//! # Example
4//!
5//! ```
6//! use option_like::option_like;
7//!
8//! option_like!(
9//! #[derive(Debug, PartialEq)]
10//! pub enum Cached<T> {
11//! Hit(T),
12//! Miss,
13//! }
14//!
15//! is_some => is_hit
16//! is_none => is_miss
17//! );
18//!
19//! // Create instances
20//! let c1 = Cached::<u32>::Hit(42);
21//! let c2 = Cached::<u32>::Miss;
22//!
23//! // Boolean tests
24//! assert!(c1.is_hit());
25//! assert!(c2.is_miss());
26//!
27//! // Convert to Option
28//! assert_eq!(Option::<u32>::from(c1), Some(42));
29//! assert_eq!(Option::<u32>::from(c2), None);
30//!
31//! // Convert from Option
32//! assert_eq!(Cached::<u32>::from(Some(42)), Cached::Hit(42));
33//! assert_eq!(Cached::<u32>::from(None), Cached::Miss);
34//! ```
35
36#![no_std]
37
38/// Creates a new enum type that behaves like Rust's `Option<T>` but with custom names.
39///
40/// This macro allows you to create your own Option-like enum with customized names for the variants
41/// and boolean test methods, while providing automatic conversions to and from the standard Option type.
42///
43/// # Parameters
44///
45/// - `$(#[$meta:meta])*`: Optional attributes to apply to the enum (e.g., `#[derive(...)]`)
46/// - `$vis`: Visibility of the enum (e.g., `pub`)
47/// - `$name`: Name of the enum (e.g., `Cached`)
48/// - `$some`: Name of the variant that holds a value (e.g., `Hit`)
49/// - `$none`: Name of the empty variant (e.g., `Miss`)
50/// - `is_some => $is_some`: Name of the method that checks if the enum holds a value (e.g., `is_hit`)
51/// - `is_none => $is_none`: Name of the method that checks if the enum is empty (e.g., `is_miss`)
52#[macro_export]
53macro_rules! option_like {
54 (
55 $(#[$meta:meta])*
56 $vis:vis enum $name:ident<T> {
57 $some:ident(T),
58 $none:ident,
59 }
60
61 is_some => $is_some:ident
62 is_none => $is_none:ident
63 ) => {
64 $(#[$meta])*
65 $vis enum $name<T> {
66 $some(T),
67 $none,
68 }
69
70 use $name::*;
71
72 impl<T> $name<T> {
73 pub fn $is_some(&self) -> bool {
74 match self {
75 $some(_) => true,
76 $none => false,
77 }
78 }
79
80 pub fn $is_none(&self) -> bool {
81 match self {
82 $some(_) => false,
83 $none => true,
84 }
85 }
86 }
87
88 impl<T> From<Option<T>> for $name<T> {
89 fn from(value: Option<T>) -> Self {
90 match value {
91 Some(inner) => $some(inner),
92 None => $none
93 }
94 }
95 }
96
97 impl<T> From<$name<T>> for Option<T> {
98 fn from(value: $name<T>) -> Option<T> {
99 match value {
100 $some(inner) => Some(inner),
101 $none => None
102 }
103 }
104 }
105 };
106}
107
108#[cfg(test)]
109mod tests {
110 option_like!(
111 #[derive(Ord, PartialOrd, Eq, PartialEq, Clone, Debug)]
112 enum Cached<T> {
113 Hit(T),
114 Miss,
115 }
116
117 is_some => is_hit
118 is_none => is_miss
119 );
120
121 const HIT: Cached<bool> = Hit(true);
122 const MISS: Cached<bool> = Miss;
123
124 #[test]
125 fn test_boolean_methods() {
126 assert!(HIT.is_hit());
127 assert!(MISS.is_miss());
128 }
129
130 #[test]
131 fn test_from() {
132 assert_eq!(Option::<bool>::from(HIT.clone()), Some(true));
133 assert_eq!(Option::<bool>::from(MISS.clone()), None);
134 assert_eq!(Cached::<bool>::from(Some(true)), Hit(true));
135 assert_eq!(Cached::<bool>::from(None), Miss);
136 }
137}