/*! **Typed early returns and loop control + Syntax sugar for try!-like error handling**
*Works with Rust v1.34+ (released on 11 April 2019)*
# Getting started
The main focus of this crate are the following three macros:
- `tear!` is used with `ValRet` for typed early returns.
- `terror!` is syntax-sugar for `try!` or the `?` operator.
- `twist!` works with `Looping` to implement typed loop control.
Look at the synopsis for a general idea of what is possible,
and then read the documentation for the macro that interests you.
Otherwise, read the `overview` module documentation that mentions *all* the things in this crate.
## Feature flags
- The "experimental" crate feature enables support for the experimental `Try` trait.
- The "combinators" crate feature adds the `side` method to the `Judge` trait. It lets you convert
to `Either` any type that implements `Judge`. You can then use `Either`'s combinators to do
what you want.
- (dev) "ignore-ui" lets you ignore error message tests because all of them are wrong as soon
as you have any warnings.
## Synopsis
Import the macros into your module:
```rust
use tear::prelude::*;
```
Explicit error-handling syntax with `terror!`:
```rust
# use tear::prelude::*;
# use std::io::{self, ErrorKind};
# fn can_error () -> Result<i32, CustomError> { Ok(1) }
# fn can_io_error () -> io::Result<i32> { Err(io::Error::new(ErrorKind::Other, "nope")) }
# fn print_error<T> (_ :T) -> CustomError { CustomError::Str("a".to_string()) }
# enum CustomError {
# Io(io::Error),
# Str(String)
# }
# fn f() -> Result<i32, CustomError> {
let handled = terror! { can_error() => print_error };
let variant = terror! { can_io_error() => CustomError::Io };
# Ok(1)
# }
// Equivalent using `?`:
# fn g() -> Result<i32, CustomError> {
let handled = can_error().map_err(print_error)?;
let variant = can_io_error().map_err(CustomError::Io)?;
# Ok(2)
# }
```
Early loop continue with `twist!`:
```
# use tear::extra::*;
# struct Regex {}
# impl Regex {
# fn new(_ :&str) -> Result<Regex, ()> { Err(()) }
# }
# let regexes_strings = vec![ "a", "b" ];
for re in regexes_strings {
// Skip iteration if the regex fails to compile
let re = twist! { Regex::new(re) => |_| next!() };
// Use regex...
# }
```
Keyword-like early returns with `tear_if!`:
```rust
# use tear::prelude::*;
fn divide_i32 (num: i32, denom: i32) -> Option<f32> {
// Return early if dividing by 0
tear_if! { denom == 0, None };
// Compute quotient...
# None
# }
```
Typed returns with `tear!`:
```rust
# use tear::prelude::*;
// Tells the calling function to return early on failure
fn get_value_or_return() -> ValRet<String, i32> { Ret(-1) }
fn status_code() -> i32 {
let v = tear! { get_value_or_return() };
// Process value...
# 1
# }
```
# See also
- [Error Handling in Rust §The real `try!` macro / `?` operator](https://blog.burntsushi.net/rust-error-handling/#the-real-try-macro-operator)
- [guard](https://docs.rs/crate/guard), a crate implementing "guard" expressions,
the opposite of `tear_if!`.
Finally, please star the [GitHub repo](https://github.com/tqdv/tear) if you found this crate useful.
It helps developer ego !
# Module documentation
Most things are public to allow easy modification. However, things intended only for module
development are marked as `(dev)`. A breaking change in those symbols *is not* a breaking change
in public API. Nonetheless, they will be documented in the changelog
In this module, we define in order
- ValRet, its implementation, and its associated trait Return
- Moral, its implementation, and its associated trait Judge
- tear!, tear_if! and terror! macros
*/
// But we use std for tests
// Documentation lints
// Clippy ignore
// Optional features
// Modules
// For documentation
// Move the trait implementations as they are quite noisy
// Currently only for `twist!`
// Utility macros that aren't the main focus. To reduce file size.
// Reexports for macros and convenience
pub use BreakValError;
pub use ;
pub use Looping;
pub use gut;
pub use Maru;
pub use From;
// For convenience, also used in prelude
use *;
use *;
use ;
/** Represents a usable value or an early return. Use with [`tear!`]
# Description
The idea is to type an early return. The early return either evaluates to something (Val) or
returns early (Ret).
*/
/**
**NB**: Other combinators such as `and`, `and_then`, `or`, `map_val`
aren't implemented because I didn't need them, not because they aren't useful.
Examples will all use the following two variables
```
# use tear::prelude::*;
let ok: ValRet<&str, &str> = Val("ok");
let error: ValRet<&str, &str> = Ret("error");
```
*/
/// Convert into [`ValRet`]
/// A notion of good and bad for the [`terror!`] macro
/** Convert from and to [`Moral`]. Used for the macro map syntax.
This mirrors the [`ops::Try`](`core::ops::Try`) trait.
It is used for the `=>` mapping syntax of macros, to differentiate the value we want to keep from
the value we want to map through the function.
*/
/** Turns a [`ValRet`] into a value or an early return
It also coerces its argument to a `ValRet` ([`Return`] trait).
# Description
```text
let x = tear! { $e };
```
If $e is `Val(v)`, then v is assigned to x. Otherwise it is `Ret(r)`, in which case
the function immediately returns with a value of r.
This macro is useful when you have functions that return ValRet.
```text
let x = tear! { $e => $f }
```
Same as the previous form, but the return value `r` is first mapped through $f before returning.
In short, we return `$f(r)`.
Additionally, both forms make use of the [`convert::From`](`core::convert::From`) trait to automatically convert
the value when returning it. This behaviour is the same as the try operator `?`.
You may need to be more specific with type annotations so that the compiler can infer the right types.
# Examples
tear! with Val and Ret.
```rust
# #[macro_use] extern crate tear;
# use tear::prelude::*;
#
// "Ian" is assigned to name
let name = tear! { Val::<_, ()>("Ian") };
# assert_eq![ name, "Ian" ];
# fn func () -> i32 {
// The function immediately returns -1
let _ = tear! { Ret(-1) };
# 0
# }
# let r = func();
# assert_eq![ r, -1 ];
```
tear! with a function returning ValRet
```rust
# #[macro_use] extern crate tear;
# use tear::prelude::*;
fn get_name () -> ValRet<String, i32> {
Val("Chris".to_string())
// or Ret(0)
}
fn func () -> i32 {
// Will either assign the value to name, or return immediately
let name = tear! { get_name() };
name.len() as i32
}
# let x = func();
# assert_eq![ x, 5 ];
```
Mapping the return value
```rust
# #[macro_use] extern crate tear;
# use std::ffi::OsString;
fn string_id(s: OsString) -> String {
let s: String = tear! { s.into_string() => |_| "No ID".to_string() };
let id = s.len().to_string();
id
}
# assert_eq![ string_id(OsString::from("ROOT")), "4" ];
```
Automatic conversion with `convert::From`
```rust
# use tear::prelude::*;
#[derive(Debug, PartialEq, Eq)]
struct MyInt(u8);
impl std::convert::From<u8> for MyInt {
fn from(x :u8) -> Self { Self(x) }
}
fn five_as_myint() -> MyInt {
tear! { Ret(5) }
}
assert_eq![ five_as_myint(), MyInt(5) ];
```
# Naming
The name "tear" comes from the image of tearing apart the the usable value from the early return.
It also happens to be that "tear" looks like "ret(urn)" backwards.
*/
/** Explicit `if` statement with early return
# Description
```text
tear_if! { cond, // <- NB: it's a comma
do_things();
v // Return value
}
```
If cond is true, it executes the statements in its body and returns its value (v here).
It's basically an early return without the return statement at the end.
```text
tear_if! { let pat = expr,
do_things();
v
}
```
You can also use the pattern matching `if let`.
# Examples
Early return a value: recursively computing the length of a slice.
```rust
# #[macro_use] extern crate tear;
fn len (v: &[i32]) -> usize {
// Base case
tear_if! { v.is_empty(), 0 as usize }
// Recursion
1 + len(&v[1..])
}
# assert_eq![ len(&[1, 2, 3]), 3 ];
```
Handle simple cases: printing help in a command line utility
```rust
# #[macro_use] extern crate tear;
use std::env;
fn main() {
let args: Vec<String> = env::args().collect();
tear_if! { args.contains(&String::from("--help")),
println!("No help available.")
}
println!("Greetings, human!");
}
```
Use patterns like `if let`
```rust
# #[macro_use] extern crate tear;
fn add_five(x: Option<i32>) -> i32 {
tear_if! { let None = x, 0 }
x.unwrap() + 5
}
assert_eq![ add_five(Some(2)), 7 ];
assert_eq![ add_five(None), 0 ];
```
*/
)
} else
}
};
// Handle tear_if! { let … }
=> ;
}
/** [`try!`]-like error-handling macro
`terror!` is like `tear!`, but stronger and more righteous.
It automatically converts the Bad value to the return type Bad value ([`Judge`] trait).
# Description
```text
let x = terror! { $e };
```
If $e is a good value, it is assigned to x. Otherwise, $e is `Bad(value)`, we return `from_bad(value)`.
This form is equivalent to the `?` operator.
```text
let x = terror! { $e => $f };
```
Same as the previous form, but the bad `value` is first mapped through $f before returning.
In short, we return `from_bad($f(value))`.
Both forms make use of the [`convert::From`](`core::convert::From`) trait to convert the bad value,
making it fully compatible with `try!` and the `?` operator.
# Explanation using examples
The description is especially terse on purpose: it is really hard to explain what `terror!` does without using examples.
## Simple examples
### Ripping Good and Bad values
`even_number` is assigned 2 because `Good(2)` is Good.
```rust
# #[macro_use] extern crate tear;
# use tear::extra::*;
fn return_two() -> Result<i32, String> {
let even_number: i32 = terror! { Good::<i32, String>(2) };
# assert_eq![ even_number, 2 ];
# Ok(even_number)
}
```
`error_five` returns early with `Err("five".to_string())` because `Bad("five".to_string())` is Bad.
```rust
# #[macro_use] extern crate tear;
# use tear::extra::*;
fn error_five() -> Result<i32, String> {
let another_number: i32 = terror! { Bad("five".to_string()) };
# Ok(5)
}
# assert_eq![ error_five(), Err("five".to_string()) ];
```
### Handling errors
Forwarding errors: If `s.into_string` is `Ok(v)`, the `String` v is assigned to s. If it is `Err(e)` with e being an `OsString`, we return `Err(e)`.
```rust
# #[macro_use] extern crate tear;
# use std::ffi::OsString;
fn len(s: OsString) -> Result<usize, OsString> {
// ┌─────────────────┐ │
// │ Result<String, OsString>
// │ └───────────┐
let s: String = terror! { s.into_string() };
Ok(s.len())
}
# assert_eq![ len(OsString::from("aa")), Ok(2) ];
```
Using a mapping function: we converts the error to the return type error
```rust
# #[macro_use] extern crate tear;
# use std::string::FromUtf8Error;
fn to_string(b: Vec<u8>) -> Result<String, String> {
let s = terror! { String::from_utf8(b) => |e: FromUtf8Error| e.utf8_error().to_string() };
Ok(s)
}
# assert_eq![ to_string(b"Zach".to_vec()), Ok("Zach".to_string()) ];
```
## The first form: `terror! { $e }`
```rust
# #[macro_use] extern crate tear;
# use std::num::ParseIntError;
fn parse_number (s :String) -> Result<i64, ParseIntError> {
// Early return on error
let n: i32 = terror! { s.parse() };
Ok(n as i64)
}
# assert_eq![ parse_number("2".to_string()), Ok(2) ];
```
In this example, `s.parse()` returns a `Result<i32, ParseIntError>`. The good value is `i32`,
and the bad value is `ParseIntError`.
If we parsed the string succesfully, `terror!` evaluates to the parsed `i32` and
it is assigned to `n`.
But if fails, the ParseIntError is returned *as an error*. This means that
our `Err::<i32, ParseIntError>` is converted to a `Err::<i64, ParseIntError>` and then returned.
This form of `terror!` is especially useful when you just want to forward the error from
a function call to the function return value. Exactly like the `?` operator.
## The second form: `terror! { $e => $f }`
```rust
# #[macro_use] extern crate tear;
# use std::num::ParseIntError;
# use std::io;
# #[derive(Debug)]
enum Error {
Parse(ParseIntError),
Io(io::Error),
}
# fn parse_number (s :String) -> Result<i64, ParseIntError> {
# // Early return on error
# let n: i32 = terror! { s.parse() };
# Ok(n as i64)
# }
#
fn square (s: String) -> Result<String, Error> {
// If parse_number fails, convert the ParseIntError into our Error type and return early
let number: i64 = terror! { parse_number(s) => Error::Parse };
// Square the number and convert it to string
let squared = (number * number).to_string();
Ok(squared)
}
# assert_eq![ square("1".to_string()).unwrap(), "1".to_string() ];
```
We now know that `parse_number` returns a `Result<i64, ParseIntError>`.
We would now like to wrap that `ParseIntError` error into our our custom `Error` error type.
To do so, we extract the `ParseIntError`, and wrap it into our custom error with `Error::Parse`.
That is the role of the function following the `=>` arrow: it converts the error type of
the left statement, into the function return error type.
### Automatic conversion just like `?`
Since `terror!` mimics `?`, it also supports autoconversion using the `convert::From` trait.
```rust
# use tear::prelude::*;
# use std::io;
# macro_rules! assert_match {
# ( $e:expr, $($p:pat)|+ ) => {
# match $e {
# $($p)|+ => (),
# ref e => panic!("assertion failed: `{:?}` does not match `{}`", e, stringify!($($p)|+)),
# }
# }
# }
# #[derive(Debug)]
enum CustomError {
IOError(io::Error),
OtherError,
}
impl std::convert::From<io::Error> for CustomError {
fn from(x :io::Error) -> Self {
CustomError::IOError(x)
}
}
# fn fail_with_io_error() -> io::Result<()> {
# Err(io::Error::new(io::ErrorKind::Other, "oh no!"))
# }
#
fn auto_convert() -> Result<bool, CustomError> {
terror! { fail_with_io_error() };
Ok(false)
}
assert_match![ auto_convert(), Err(CustomError::IOError(_)) ];
```
# `terror!` vs. `?` when moving into closures
The only difference between `terror!` and `?` is that since `terror!` is a macro,
you can move variables into the closure without the borrow checker yelling at you.
In this example, we want to return an error built from `path` using the `?` operator.
```compile_fail
# use std::{fs::File, path::PathBuf};
# enum Error {
# OpenF(PathBuf),
# }
#
fn open_file(path: PathBuf) -> Result<(), Error> {
let file = File::open(&path).map_err(|_| Error::OpenF(path))?;
// Do stuff with path and file
# drop(path); drop(file);
# Ok(())
}
```
However, it fails to compile with the message `` error[E0382]: use of moved value: `path` ``.
This is because the borrow checker can't tell that when the closure is called,
it immediately returns. It sees that `path` is moved into the closure, and refuses
to let you use it in the rest of the function.
But if works if we use `terror!`. That's because since it's a macro, it expands into
code that tells the compiler that we immediately return after calling the closure.
```
# #[macro_use] extern crate tear;
# use std::{fs::File, path::PathBuf};
# enum Error {
# OpenF(PathBuf),
# }
#
fn open_file(path: PathBuf) -> Result<(), Error> {
let file = terror! { File::open(&path) => |_| Error::OpenF(path) };
// Do stuff with path and file
# drop(path); drop(file);
# Ok(())
}
```
# Naming
The name terror comes from "return error" and "tear! error".
The mnemonic was "When you need to scream an error from the inside" because of how closures worked (see §`terror!` vs. `?` when moving into closures).
*/