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}