inner/lib.rs
1//! The `inner!` macro makes descending into an enum variant
2//! more ergonomic.
3//!
4//! The `some!` and `ok!` macros turn your enum into an `Option` and `Result`, respectively.
5//!
6//! # Helpful unwrap
7//! The simplest case for `inner!` is almost like unwrap:
8//!
9//! ```
10//! # #[macro_use] extern crate inner;
11//! # fn main() {
12//! let x = Some(1);
13//! let y: Result<_, ()> = Ok(2);
14//! assert_eq!(inner!(x), 1);
15//! assert_eq!(inner!(y), 2);
16//! # }
17//! ```
18//!
19//! ...but if you instead use it on a `None` or `Err` value:
20//!
21//! ```ignore
22//! let z = None;
23//! let y = inner!(z);
24//! ```
25//!
26//! ...it will panic, with an error message that points you to a more
27//! helpful location than some line number inside libcore:
28//!
29//! ```ignore
30//! thread "test" panicked at "Unexpected value found inside "z"", src/lib.rs:23
31//! ```
32//!
33//! # Error handling
34//! If panic isn't an option - and it usually isn't - just add an `else` clause:
35//!
36//! ```
37//! # #[macro_use] extern crate inner;
38//! # fn main() {
39//! let x: Result<String, i32> = Err(7);
40//! let y = inner!(x, else { return });
41//! // Since x is an Err, we'll never get here.
42//! println!("The string length is: {}", y.len());
43//! # }
44//! ```
45//!
46//! You can use the else clause to compute a default value, or use flow control
47//! (e g `break`, `continue`, or `return`).
48//!
49//! Want access to what's inside the `Err` value in your `else` clause?
50//! No problem, just add a `|variable|` after `else`, like this:
51//!
52//! ```
53//! # #[macro_use] extern crate inner;
54//! # fn main() {
55//! let x: Result<String, i32> = Err(7);
56//! let y = inner!(x, else |e| {
57//! assert_eq!(e, 7);
58//! (e + 2).to_string()
59//! });
60//! assert_eq!(&y, "9");
61//! # }
62//! ```
63//!
64//! Note: This does not turn your else clause into a closure, so you can still use
65//! (e g) `return` the same way as before.
66//!
67//! # It works with your enums too
68//! It does not work only with `Option` and `Result`. Just add an `if` clause:
69//!
70//! ```
71//! # #[macro_use] extern crate inner;
72//! # fn main() {
73//! enum Fruit {
74//! Apple(i32),
75//! Orange(i16),
76//! }
77//!
78//! let z = Fruit::Apple(15);
79//! let y = inner!(z, if Fruit::Apple, else {
80//! println!("I wanted an apple and I didn't get one!");
81//! 0
82//! });
83//! assert_eq!(y, 15);
84//! # }
85//! ```
86//!
87//! You can skip the `else` clause to panic in case the enum is not
88//! the expected variant.
89//!
90//! Note that in this case, the entire item (instead of the contents inside
91//! `Err`) is passed on to the `else` clause:
92//!
93//! ```
94//! # #[macro_use] extern crate inner;
95//! # fn main() {
96//! #[derive(Eq, PartialEq, Debug)]
97//! enum Fruit {
98//! Apple(i32),
99//! Orange(i16),
100//! }
101//!
102//! let z = Fruit::Orange(15);
103//! inner!(z, if Fruit::Apple, else |e| {
104//! assert_eq!(e, Fruit::Orange(15));
105//! return;
106//! });
107//! # }
108//! ```
109//!
110//! Another option is to implement this crate's `IntoResult` trait for
111//! your enum. Then you don't have to write an `if` clause to tell what
112//! enum variant you want to descend into, and you can choose more than
113//! one enum variant to be `Ok`:
114//!
115//! ```ignore
116//! enum Fruit {
117//! Apple(i32),
118//! Orange(i16),
119//! Rotten,
120//! }
121//!
122//! impl IntoResult<i32, ()> for Fruit {
123//! fn into_result(self) -> Result<i32, ()> {
124//! match self {
125//! Fruit::Apple(i) => Ok(i),
126//! Fruit::Orange(i) => Ok(i as i32),
127//! Fruit::Rotten => Err(()),
128//! }
129//! }
130//! }
131//!
132//! assert_eq!(9, inner!(Fruit::Apple(9)));
133//! ```
134//!
135//! # License
136//! Apache2.0/MIT
137
138/// Converts a value into a Result.
139/// You can implement this for your own types if you want
140/// to use the `inner!` macro in more ergonomic ways.
141pub trait IntoResult<T, E> {
142 fn into_result(self) -> Result<T, E>;
143}
144
145
146/*
147// Impossible due to conflicting impls :-(
148impl<T, E, Z> IntoResult<T, E> for Z where Z: Into<Result<T, E>> {
149 fn into_result(self) -> Result<T, E> { self.into() }
150}
151*/
152
153impl<T, E> IntoResult<T, E> for Result<T, E> {
154 #[inline]
155 fn into_result(self) -> Result<T, E> { self }
156}
157
158impl<T> IntoResult<T, ()> for Option<T> {
159 #[inline]
160 fn into_result(self) -> Result<T, ()> { self.ok_or(()) }
161}
162
163/// The `inner!` macro - see module level documentation for details.
164#[macro_export]
165macro_rules! inner {
166 ($x:expr, if $i:path, else |$e:ident| $b:block) => {
167 {
168 match $x {
169 $i(q) => q,
170 $e @ _ => $b,
171 }
172 }
173 };
174
175 ($x:expr, if $i:path, else $b:block) => {
176 {
177 match $x {
178 $i(q) => q,
179 _ => $b,
180 }
181 }
182 };
183
184 ($x:expr, else |$e:ident| $b:block) => {
185 {
186 use $crate::IntoResult;
187 match $x.into_result() {
188 Ok(q) => q,
189 Err($e) => $b,
190 }
191 }
192 };
193
194 ($x:expr, else $b:block) => {
195 {
196 use $crate::IntoResult;
197 match $x.into_result() {
198 Ok(q) => q,
199 _ => $b,
200 }
201 }
202 };
203
204 ($x:expr, if $i:path) => {
205 {
206 match $x {
207 $i(q) => q,
208 _ => panic!("Unexpected value found inside '{}'", stringify!($x)),
209 }
210 }
211 };
212
213 ($x:expr) => {
214 {
215 use $crate::IntoResult;
216 match $x.into_result() {
217 Ok(q) => q,
218 _ => panic!("Unexpected value found inside '{}'", stringify!($x)),
219 }
220 }
221 };
222}
223
224/// Converts your enum to an Option.
225///
226/// # Examples
227///
228/// ```ignore
229/// assert_eq!(some!(Fruit::Apple(15), if Fruit::Apple), Some(15));
230/// assert_eq!(some!(Fruit::Orange(5), if Fruit::Apple), None);
231/// ```
232#[macro_export]
233macro_rules! some {
234 ($x:expr, if $i:path, else |$e:ident| $b:block) => {
235 {
236 match $x {
237 $i(q) => Some(q),
238 $e @ _ => $b,
239 }
240 }
241 };
242
243 ($x:expr, if $i:path, else $b:block) => {
244 {
245 match $x {
246 $i(q) => Some(q),
247 _ => $b,
248 }
249 }
250 };
251
252 ($x:expr, if $i:path) => {
253 {
254 match $x {
255 $i(q) => Some(q),
256 _ => None,
257 }
258 }
259 };
260}
261
262/// Converts your enum to an Result.
263///
264/// # Examples
265///
266/// ```ignore
267/// assert_eq!(ok!(Fruit::Apple(15), if Fruit::Apple), Ok(15));
268/// assert_eq!(ok!(Fruit::Orange(5), if Fruit::Apple), Err(Fruit::Orange(5)));
269///
270/// assert_eq!(ok!(Fruit::Orange(5), if Fruit::Apple, or {75}), Err(75));
271/// assert_eq!(ok!(Fruit::Orange(5), if Fruit::Apple, else {Err(75)}), Err(75));
272/// ```
273#[macro_export]
274macro_rules! ok {
275 ($x:expr, if $i:path, else |$e:ident| $b:block) => {
276 {
277 match $x {
278 $i(q) => Ok(q),
279 $e @ _ => $b,
280 }
281 }
282 };
283
284 ($x:expr, if $i:path, else $b:block) => {
285 {
286 match $x {
287 $i(q) => Ok(q),
288 _ => $b,
289 }
290 }
291 };
292
293 ($x:expr, if $i:path, or |$e:ident| $b:block) => {
294 {
295 match $x {
296 $i(q) => Ok(q),
297 $e @ _ => Err($b),
298 }
299 }
300 };
301
302 ($x:expr, if $i:path, or $b:block) => {
303 {
304 match $x {
305 $i(q) => Ok(q),
306 _ => Err($b),
307 }
308 }
309 };
310
311 ($x:expr, if $i:path) => {
312 {
313 match $x {
314 $i(q) => Ok(q),
315 n @ _ => Err(n),
316 }
317 }
318 };
319}
320
321
322#[test]
323fn simple_opt() {
324 assert_eq!(inner!(Some(7)), 7);
325}
326
327#[test]
328#[should_panic]
329fn simple_opt_fail() {
330 let z: Option<i32> = None;
331 inner!(z);
332}
333
334#[test]
335fn else_clause() {
336 let x: Result<String, i32> = Err(7);
337 let _ = inner!(x, else { return });
338 panic!();
339}
340
341#[test]
342fn else_clause_2() {
343 let x: Result<String, i32> = Err(7);
344 let y = inner!(x, else |e| {
345 assert_eq!(e, 7);
346 (e + 2).to_string()
347 });
348 assert_eq!(&y, "9");
349}
350
351#[test]
352fn apple() {
353 enum Fruit {
354 Apple(i32),
355 _Orange(i16),
356 }
357 let z = Fruit::Apple(15);
358 assert_eq!(15, inner!(z, if Fruit::Apple));
359}
360
361#[test]
362fn if_else() {
363 enum Fruit {
364 Apple(i32),
365 _Orange(i16),
366 }
367 let z = Fruit::Apple(15);
368 assert_eq!(15, inner!(z, if Fruit::Apple, else {
369 panic!("Not an apple");
370 }));
371}
372
373#[test]
374fn own_enum() {
375 #[derive(Debug, PartialEq, Eq)]
376 enum Fruit {
377 Apple(i32),
378 Orange(i16),
379 }
380
381 impl IntoResult<i32, i16> for Fruit {
382 fn into_result(self) -> Result<i32, i16> {
383 match self {
384 Fruit::Apple(i) => Ok(i),
385 Fruit::Orange(i) => Err(i),
386 }
387 }
388 }
389 let z = Fruit::Orange(15);
390 assert_eq!(7, inner!(z, else |e| { (e - 8) as i32 }));
391
392 let z = Fruit::Apple(15);
393 assert_eq!(9, inner!(z, if Fruit::Orange, else |e| {
394 assert_eq!(e, Fruit::Apple(15));
395 9
396 }));
397
398}
399
400#[test]
401fn some() {
402
403 #[derive(Debug, PartialEq, Eq)]
404 enum Fruit {
405 Apple(i32),
406 Orange(i16),
407 }
408
409 assert_eq!(some!(Fruit::Apple(15), if Fruit::Apple), Some(15));
410 assert_eq!(some!(Fruit::Orange(15), if Fruit::Apple), None);
411 assert_eq!(some!(Fruit::Orange(15), if Fruit::Apple, else |e| {
412 assert_eq!(e, Fruit::Orange(15));
413 Some(30)
414 }), Some(30));
415}
416
417#[test]
418fn ok() {
419
420 #[derive(Debug, PartialEq, Eq)]
421 enum Fruit {
422 Apple(i32),
423 Orange(i16),
424 }
425
426 assert_eq!(ok!(Fruit::Apple(15), if Fruit::Apple), Ok(15));
427
428 assert_eq!(ok!(Fruit::Orange(15), if Fruit::Apple), Err(Fruit::Orange(15)));
429 assert_eq!(ok!(Fruit::Orange(15), if Fruit::Apple, else |e| {
430 assert_eq!(e, Fruit::Orange(15));
431 Err(3)
432 }), Err(3));
433
434 assert_eq!(ok!(Fruit::Apple(15), if Fruit::Orange, or {67}), Err(67));
435 assert_eq!(ok!(Fruit::Apple(15), if Fruit::Apple, or {67}), Ok(15));
436}
437