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