1#![no_std]
37#![cfg_attr(not(test), deny(unused_crate_dependencies))]
38
39#[macro_export]
41macro_rules! option_like_impl {
42 (
43 $name:ident,
44 $none:ident,
45 $some:ident,
46 $is_none:ident,
47 $is_some:ident $(,)?
48 ) => {
49 impl<T> $name<T> {
50 pub fn $is_none(&self) -> bool {
51 match self {
52 Self::$none => true,
53 Self::$some(_) => false,
54 }
55 }
56
57 pub fn $is_some(&self) -> bool {
58 match self {
59 Self::$none => false,
60 Self::$some(_) => true,
61 }
62 }
63
64 #[inline]
65 pub fn map<U, F>(self, f: F) -> $name<U>
66 where
67 F: FnOnce(T) -> U,
68 {
69 match self {
70 Self::$none => $name::$none,
71 Self::$some(x) => $name::$some(f(x)),
72 }
73 }
74
75 #[inline(always)]
76 #[track_caller]
77 pub fn unwrap(self) -> T {
78 match self {
79 Self::$none => Self::unwrap_failed(),
80 Self::$some(val) => val,
81 }
82 }
83
84 #[inline]
85 pub fn unwrap_or_default(self) -> T
86 where
87 T: Default,
88 {
89 match self {
90 Self::$none => T::default(),
91 Self::$some(x) => x,
92 }
93 }
94
95 #[inline]
96 #[track_caller]
97 pub fn unwrap_or_else<F>(self, f: F) -> T
98 where
99 F: FnOnce() -> T,
100 {
101 match self {
102 Self::$none => f(),
103 Self::$some(x) => x,
104 }
105 }
106
107 #[inline]
108 #[track_caller]
109 pub fn expect(self, msg: &str) -> T {
110 match self {
111 Self::$none => Self::expect_failed(msg),
112 Self::$some(val) => val,
113 }
114 }
115
116 #[cold]
117 #[track_caller]
118 const fn unwrap_failed() -> ! {
119 panic!(stringify!("called `", $name, "::unwrap()` on a `", $none, "` value"))
120 }
121
122 #[cold]
123 #[track_caller]
124 const fn expect_failed(msg: &str) -> ! {
125 panic!("{}", msg)
126 }
127 }
128 };
129}
130
131#[macro_export]
133macro_rules! option_like_from_into_option {
134 (
135 $name:ident,
136 $none:ident,
137 $some:ident $(,)?
138 ) => {
139 impl<T> From<Option<T>> for $name<T> {
140 fn from(value: Option<T>) -> Self {
141 match value {
142 None => Self::$none,
143 Some(inner) => Self::$some(inner),
144 }
145 }
146 }
147
148 impl<T> From<$name<T>> for Option<T> {
149 fn from(value: $name<T>) -> Option<T> {
150 match value {
151 $name::$none => None,
152 $name::$some(inner) => Some(inner),
153 }
154 }
155 }
156 };
157}
158
159#[macro_export]
174macro_rules! option_like {
175 (
176 $(#[$meta:meta])*
177 $vis:vis enum $name:ident<T> {
178 $(#[$none_meta:meta])*
179 $none:ident,
180 $(#[$some_meta:meta])*
181 $some:ident(T),
182 }
183
184 is_none => $is_none:ident
185 is_some => $is_some:ident
186 ) => {
187 $(#[$meta])*
188 $vis enum $name<T> {
189 $(#[$none_meta])*
190 $none,
191 $(#[$some_meta])*
192 $some(T),
193 }
194
195 $crate::option_like_impl!(
196 $name,
197 $none,
198 $some,
199 $is_none,
200 $is_some,
201 );
202
203 $crate::option_like_from_into_option!(
204 $name,
205 $none,
206 $some,
207 );
208 };
209}
210
211#[cfg(test)]
212mod tests {
213 option_like!(
214 #[derive(Ord, PartialOrd, Eq, PartialEq, Default, Clone, Debug)]
215 enum Cached<T> {
216 #[default]
217 Miss,
218 Hit(T),
219 }
220
221 is_none => is_miss
222 is_some => is_hit
223 );
224
225 use Cached::*;
226
227 fn hit() -> Cached<bool> {
228 Hit(true)
229 }
230
231 fn miss() -> Cached<bool> {
232 Miss
233 }
234
235 #[test]
236 fn test_boolean_methods() {
237 assert!(hit().is_hit());
238 assert!(miss().is_miss());
239 }
240
241 #[test]
242 fn test_from() {
243 assert_eq!(Option::<bool>::from(hit()), Some(true));
244 assert_eq!(Option::<bool>::from(miss()), None);
245 assert_eq!(Cached::<bool>::from(Some(true)), Hit(true));
246 assert_eq!(Cached::<bool>::from(None), Miss);
247 }
248
249 #[test]
250 fn test_map() {
251 assert_eq!(hit().map(|t| !t), Hit(false));
252 assert_eq!(miss().map(|t| !t), Miss);
253 }
254
255 #[test]
256 fn test_unwrap_or_default() {
257 assert!(hit().unwrap_or_default());
258 assert!(!miss().unwrap_or_default());
259 }
260
261 #[test]
262 fn test_unwrap_or_else() {
263 assert!(hit().unwrap_or_else(|| false));
264 assert!(miss().unwrap_or_else(|| true));
265 }
266
267 #[test]
268 fn test_unwrap_no_panic() {
269 assert!(hit().unwrap());
270 }
271
272 #[test]
273 #[should_panic]
274 fn test_unwrap_panic() {
275 miss().unwrap();
276 }
277
278 #[test]
279 fn test_expect_no_panic() {
280 assert!(hit().expect("should not panic"));
281 }
282
283 #[test]
284 #[should_panic]
285 fn test_expect_panic() {
286 miss().expect("should panic");
287 }
288}