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