# BetterResult

#### Empowered error handling capabilities over rust default result methodology.

BetterResult is a monadic error handling framework designed around Rust's philosophy over
ownership, life cycles and type safety, providing the power of functional error chaining with
Rust's protocol declaration safety.

It also provides two declarative macros to work as the error composing from thiserror, and error 
chaining from nightly trait `Try`.

### Architecture

BetterResult works like a state machine over result values that safely routes event chaining, 
while still respecting Rusts type, ownership, and borrow checking systems.

```text
             [BetterError(State - Active)]----------------
                         |                               |
             -----------------------------               |
             |                           |               |
   [Manipulate Inner xN+1]        [Extract Inner]    [Default Pattern matching]
             |                           |               |
        [Catch Err]                 [Catch Err]      {Complete}
             |                           |
     [finally operation]            {Complete}
             |
         {Complete}
```
 
The state machine is a Generic Const Expression that stores empty structs, these acts as the
current state of the enum and are sealed in a private module inside the crate, to prevent custom
chain states creation

Each relevant node in the chain call returns Self with a new state:
- Manipulating inner does not change the state, allowing for multiple `then()` in the chain.
- Catch functions in state `Active` changes the state to `ErrEvaluated`, which triggers finally
operations.
- Finally operations invariantly extract `BetterResult::Val` from BetterResult if it exists.
- - If an error is catch before finally, the error is extracted to be treated in the catch
closure and finally doesn't execute

On the other hand:
- Extracting inner on `Active` returns state `ValConsumed`, which triggers a catch chain.
- Catching errors in `ValConsumed` automatically consumes the error to be treated in the caller
side.

All these behaviours are enforced at compile time by the borrow checker due to the state machine
stored in the marker.

### Interoperability

Rust default `Result` can be converted to BetterResult by pattern matching the respective
values into `BetterResult::succeed` (for `Ok` values), or `BetterResult::fail` (for `Err` 
values):

```rust
match func() {
    Ok(v) => BetterResult::succeed(v),
    Err(e) => BetterResult::fail(SomeErrorType(e) || e),
}

// On the BetterResult side, the enum can be pattern matched just like results:

match better_result_func() {
    BetterResult::Val(v) => Ok(v),
    BetterResult::Err(e) => Err(e),
}

// The same can be done using the monadic chain.
better_result_func()
    .then(|v| {//...do some stuff})
    .catch_as::<_, Result<T, K>>(|e| return Err(e)) // catch any errors and return as Result::Err
    .finally_return_as::<_, Result<T, K>>(|v| return Ok(v)); // if passed, return Result::Ok
```

### Usage
```toml
[dependencies]
better_result = "0.1"
```

```rust
use better_result::{ BetterResult, better_error, chain_up};

// error declaration using better_error!
// Display, Error and From<T> are implemented automaticaly
better_error! {
    #[derive(Debug)]
    enum SomeError {
        ErrorValOne => "Some error message",
        ErrorValTwo {
            path: String,
            reason: String,
        } => "Error accessing {path}: {reason}",
        ErrorValThree {
            error_no: i32,
            is_true: bool
        } => "Error {error_no}: Happened? {is_true}",
    }
    from {
        Inner(SomeErrorInner) // Inner will become a variant that stores SomeErrorInner
    }
}

// using the error
let val: Option<T> = better_error_func()
    .then(|v| transform(v))
    .then(|v| aggregate(v))
    .catch_as::<_, SomeOtherError>(|e| {
        match e {
            ErrorValOne(e) => return SomeOtherError::One(format!("{:#?}", e)),
            ErrorValTwo(_) => return SomeOtherError::Two,
            ErrorValThree(_) => panic!("This is unrecoverable"),
        }
    }) // catch will stop at any error from the `then()` chain regardless of the step it happened.
    .finally(|v| v); // finally will only execute if catch doesn't find any errors.

// chaining the error up
let val: T = chain_up!(better_error_func()) // will either assing T to the variable, or return the error.
```

### License
MIT
