├── Cargo.toml ├── README.md └── src └── lib.rs /Cargo.toml: -------------------------------------------------------------------------------- 1 | [package] 2 | name = "inner" 3 | version = "0.1.1" 4 | authors = ["David Henningsson "] 5 | license = "Apache-2.0/MIT" 6 | description = "The inner! macro descends into an enum variant. It's more flexible than try!() and unwrap(), and it works with your enum, too!" 7 | documentation = "http://diwic.github.io/rs-docs/inner/index.html" 8 | repository = "https://github.com/diwic/inner-rs" 9 | keywords = ["unwrap", "try", "let", "else"] 10 | 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | The `inner!` macro makes descending into an enum variant 2 | more ergonomic. The `some!` and `ok!` macros turns an enum 3 | into an `Option` or `Result`. 4 | 5 | [Crates.io](https://crates.io/crates/inner/) 6 | 7 | [API Documentation](http://diwic.github.io/rs-docs/inner/index.html) 8 | 9 | # Helpful unwrap 10 | The simplest case is almost like unwrap: 11 | 12 | ```rust 13 | let x = Some(1); 14 | let y: Result<_, ()> = Ok(2); 15 | assert_eq!(inner!(x), 1); 16 | assert_eq!(inner!(y), 2); 17 | ``` 18 | 19 | ...but if you instead use it on a `None` or `Err` value: 20 | 21 | ```rust 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 | ``` 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 | ```rust 37 | let x: Result = Err(7); 38 | let y = inner!(x, else { return }); 39 | // Since x is an Err, we'll never get here. 40 | println!("The string length is: {}", y.len()); 41 | ``` 42 | 43 | You can use the else clause to compute a default value, or use flow control 44 | (e g `break`, `continue`, or `return`). 45 | 46 | Want access to what's inside the `Err` value in your `else` clause? 47 | No problem, just add a `|variable|` after `else`, like this: 48 | 49 | ```rust 50 | let x: Result = Err(7); 51 | let y = inner!(x, else |e| { 52 | assert_eq!(e, 7); 53 | (e + 2).to_string() 54 | }); 55 | assert_eq!(&y, "9"); 56 | ``` 57 | 58 | Note: This does not turn your else clause into a closure, so you can still use 59 | (e g) `return` the same way as before. 60 | 61 | # It works with your enums too 62 | 63 | It does not work only with `Option` and `Result`. Just add an `if` clause: 64 | 65 | ```rust 66 | enum Fruit { 67 | Apple(i32), 68 | Orange(i16), 69 | } 70 | 71 | let z = Fruit::Apple(15); 72 | let y = inner!(z, if Fruit::Apple, else { 73 | println!("I wanted an apple and I didn't get one!"); 74 | 0 75 | }); 76 | assert_eq!(y, 15); 77 | ``` 78 | 79 | You can skip the `else` clause to panic in case the enum is not 80 | the expected variant. 81 | 82 | Note that in this case, the entire item (instead of the contents inside 83 | `Err`) is passed on to the `else` clause: 84 | 85 | ```rust 86 | #[derive(Eq, PartialEq, Debug)] 87 | enum Fruit { 88 | Apple(i32), 89 | Orange(i16), 90 | } 91 | 92 | let z = Fruit::Orange(15); 93 | inner!(z, if Fruit::Apple, else |e| { 94 | assert_eq!(e, Fruit::Orange(15)); 95 | return; 96 | }); 97 | ``` 98 | 99 | You can also turn your enum into a `Option` with the `Some` macro: 100 | 101 | ```rust 102 | assert_eq!(some!(Fruit::Apple(15), if Fruit::Apple), Some(15)); 103 | assert_eq!(some!(Fruit::Orange(5), if Fruit::Apple), None); 104 | assert_eq!(some!(Fruit::Orange(5), if Fruit::Apple, else |e| {Some(e + 2)}), Some(7)); 105 | ``` 106 | 107 | Or into a `Result` with the `ok!()` macro: 108 | 109 | ```rust 110 | assert_eq!(ok!(Fruit::Apple(15), if Fruit::Apple), Ok(15)); 111 | assert_eq!(ok!(Fruit::Orange(5), if Fruit::Apple), Err(Fruit::Orange(5))); 112 | 113 | assert_eq!(ok!(Fruit::Orange(5), if Fruit::Apple, or |e| {e + 70}), Err(75)); 114 | assert_eq!(ok!(Fruit::Orange(5), if Fruit::Apple, else {Err(75)}), Err(75)); 115 | ``` 116 | 117 | Notice that the `ok!()` macro has an optional `or` clause that encapsulates the 118 | expression in an `Err`, whereas the `else` clause gives you maximum flexibility 119 | to return either an `Err` or an `Ok`. 120 | 121 | 122 | Another option is to implement this crate's `IntoResult` trait for 123 | your enum. Then you don't have to write an `if` clause to tell what 124 | enum variant you want to descend into, and you can choose more than 125 | one enum variant to be `Ok`: 126 | 127 | ```rust 128 | enum Fruit { 129 | Apple(i32), 130 | Orange(i16), 131 | Rotten, 132 | } 133 | 134 | impl IntoResult for Fruit { 135 | fn into_result(self) -> Result { 136 | match self { 137 | Fruit::Apple(i) => Ok(i), 138 | Fruit::Orange(i) => Ok(i as i32), 139 | Fruit::Rotten => Err(()), 140 | } 141 | } 142 | } 143 | 144 | assert_eq!(9, inner!(Fruit::Apple(9))); 145 | ``` 146 | 147 | # License 148 | Apache2.0/MIT 149 | 150 | -------------------------------------------------------------------------------- /src/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 = 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 = 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 for Fruit { 123 | //! fn into_result(self) -> Result { 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. 141 | pub trait IntoResult { 142 | fn into_result(self) -> Result; 143 | } 144 | 145 | 146 | /* 147 | // Impossible due to conflicting impls :-( 148 | impl IntoResult for Z where Z: Into> { 149 | fn into_result(self) -> Result { self.into() } 150 | } 151 | */ 152 | 153 | impl IntoResult for Result { 154 | #[inline] 155 | fn into_result(self) -> Result { self } 156 | } 157 | 158 | impl IntoResult for Option { 159 | #[inline] 160 | fn into_result(self) -> Result { self.ok_or(()) } 161 | } 162 | 163 | /// The `inner!` macro - see module level documentation for details. 164 | #[macro_export] 165 | macro_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] 233 | macro_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] 274 | macro_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] 323 | fn simple_opt() { 324 | assert_eq!(inner!(Some(7)), 7); 325 | } 326 | 327 | #[test] 328 | #[should_panic] 329 | fn simple_opt_fail() { 330 | let z: Option = None; 331 | inner!(z); 332 | } 333 | 334 | #[test] 335 | fn else_clause() { 336 | let x: Result = Err(7); 337 | let _ = inner!(x, else { return }); 338 | panic!(); 339 | } 340 | 341 | #[test] 342 | fn else_clause_2() { 343 | let x: Result = 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] 352 | fn 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] 362 | fn 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] 374 | fn own_enum() { 375 | #[derive(Debug, PartialEq, Eq)] 376 | enum Fruit { 377 | Apple(i32), 378 | Orange(i16), 379 | } 380 | 381 | impl IntoResult for Fruit { 382 | fn into_result(self) -> Result { 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] 401 | fn 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] 418 | fn 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 | 438 | --------------------------------------------------------------------------------