//! # BetterResult
//!
//! #### Empowered error handling capabilities over rust default result methodology.
//!
//! The struct encapsulates two optional values for [`BetterResult::Err`] and [`BetterResult::Val`],
//! and adds monadic chain expression for extensive manipulation of the values inside.
//!
//! ### 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:
//!
//! ```rust
//! match better_result_func() {
//!     BetterResult::Val(v) => Ok(v),
//!     BetterResult::Err(e) => Err(e),
//! }
//! ```
//!
//! The same can be done using the monadic chain.
//!
//! ```rust
//! better_result_func()
//!     .then(|v| transform(v))
//!     .catch_as::<_, Result<T, K>>(|e| return Err(e))
//!     .finally_as::<_, Result<T, K>>(|v| return Ok(v));
//! ```
//!
//! ### Macros
//!
//! BetterResult ship with two quality of life macros to further improve error handling.
//! [`better_error`] and [`chain_up`].
//!
//! *better_error!* allows for powerfull error composing, adopting thiserror methodology for enum
//! declaration, but in a declarative macro that lives inside the main crate. Additionally, an
//! optional `from` field is provided in the macro to provide error type casting for foreing error
//! encapsulation:
//!
//! ```rust
//! better_error! {
//!     #[derive(Debug)]
//!     pub enum SomeError {
//!         SomeErrorValueOne { 
//!             path: String,
//!             reason: String,
//!         } => "Some error reason",
//!         SomeErrorValueTwo {
//!             error_no: u16,
//!             call: CallVariantsEnum,
//!         } => "Some other error reason",
//!         SomeErrorValueThree => "Another very valid error reason"
//!     }
//!     from {
//!         SomeModuleInner(SomeModuleError),
//!         Io(std::error::Error),
//!     }
//! }
//! ```
//!
//! *chain_up!* works exactly like `?` does in the nightly `Try` trait. As this is still unstable and
//! accessible only through nightly builds according to information at the development time of this
//! crate, so i implemented my own version that roughly mimicks the behaviour while maintaining rust
//! type safety.
//!
//! ```rust
//! let some_val = chain_up!(better_result_func());
//! ```

/// Chain state structs,
///
/// They are encapsulated into a sealed mod to avoid curious little callers from constructing their
/// own chain state from scratch and breaking everything... i see you.
mod sealed {
    pub struct Active;
    pub struct ValConsumed;
    pub struct ErrEvaluated;
}

/// BetterResult enum
///
/// Contains the optional results for value and error as well as a phantom marked chain state that
/// is mutable only internally. It also holds all monadic chain functions for extended error
/// handling capabilities.
///
/// Default state is Active.
#[derive(Debug)]
pub enum BetterResult<T, K, State = sealed::Active> {
    Val(Option<T>),
    Err(Option<K>),
    _State(std::marker::PhantomData<State>),
}

// General BetterResult functions. Applies to all states
impl<T, K> BetterResult<T, K> 
where
    K: std::fmt::Display + std::error::Error,
{
    /// Casts [`BetterResult`] into a standard rust [`Result`].
    ///
    /// ### Returns:
    /// A result containing either the result value or an error.
    pub fn diverge(self) -> Result<T, K> {
        match self {
            BetterResult::Val(Some(v)) => Ok(v),
            BetterResult::Err(Some(e)) => Err(e),
            _ => panic!("Something went terribly wrong here.")
        }
    }

    /// Casts [`Result`] into a [`BetterResult`].
    ///
    /// ### Returns:
    /// A BetterResult variant in state active.
    pub fn converge(res: Result<T, K>) -> BetterResult<T, K, sealed::Active> 
    {
        match res {
            Ok(v) => BetterResult::Val(Some(v)),
            Err(e) => BetterResult::Err(Some(e)),
        }
    }

    /// Check if BetterResult is on Err variant.
    ///
    /// ### Returns:
    /// A bool stating if the result is of variant Err
    pub fn is_err(self) -> bool {
        match self {
            BetterResult::Err(_) => true,
            _ => false,
        }
    }

    /// Check if BetterResult is on Val variant.
    ///
    /// ### Returns:
    /// A bool stating if the result is of variant Val
    pub fn is_ok(self) -> bool {
        match self {
            BetterResult::Val(_) => true,
            _ => false,
        }
    }
}

// Active chain state. The result has just been queried and needs to be handled somehow.
impl<T, K> BetterResult<T, K, sealed::Active>
where
    K: std::fmt::Display + std::error::Error,
{
    /// Fails the call and returns a [`BetterResult::Err`] wrapping the error value.
    ///
    /// ### Params:
    /// @err: the error value to be wrapped and returned.
    ///
    /// ### Returns:
    /// BetterResult::Err variant.
    pub fn fail(err: K) -> Self {
        return BetterResult::Err(Some(err));
    }

    /// Succeeds the call and returns a [`BetterResult::Val`] wrapping the value.
    ///
    /// ### Params:
    /// @val: The result value to wrapped and returned.
    ///
    /// ### Returns:
    /// BetterResult::Val variant.
    pub fn succeed(val: T) -> Self {
        return BetterResult::Val(Some(val));
    }

    /// Wraps a mutable reference to the result value in a caller provided closure to be handled.
    ///
    /// This call mutates the value internally and returns the same instance of Self passed. It can 
    /// be used to chain mutations that can fail sequentially and handle all errors once. If an error 
    /// is found instead of a result value, the closure is skipped to the catch phase.
    ///
    /// ### Params:
    /// @f: a closure to manipulate the inner value.
    ///
    /// ### Returns:
    /// The same instance of Self passed.
    pub fn then<F>(self, f: F) -> Self
    where
        F: FnOnce(&mut T),
    {
        match self {
            BetterResult::Val(Some(mut v)) => {
                f(&mut v);
                BetterResult::Val(Some(v))
            },
            BetterResult::Err(Some(e)) => BetterResult::<T, K, sealed::Active>::Err(Some(e)),
            _ => panic!("Something went terribly wrong here."),
        }
    }

    /// Wraps a mutable reference to the result value in a caller provided closure to be handled.
    /// This closure can mutate the value into a new type.
    ///
    /// This call mutates the value internally and returns an instance of Self with the new value
    /// type. It canbe used to chain mutations that can fail sequentially and handle all errors once. 
    /// If an error is found instead of a result value, the closure is skipped to the catch phase.
    ///
    /// ### Params:
    /// @f: a closure to manipulate the inner value.
    /// @NewT: the new type of the value, passed as a typed argument.
    ///
    /// ### Returns:
    /// An instance of Self with the new type value.
    pub fn then_as<F, NewT>(self, f: F) -> BetterResult<NewT, K, sealed::Active>
    where
        F: FnOnce(&mut T) -> BetterResult<NewT, K, sealed::Active>,
    {
        match self {
            BetterResult::Val(Some(mut v)) => {
                f(&mut v)
            },
            BetterResult::Err(Some(e)) => BetterResult::<NewT, K, sealed::Active>::Err(Some(e)),
            _ => panic!("Something went terribly wrong here."),
        }
    }

    /// Consumes the value into a then operation and changes the state to `ValConsumed`.
    ///
    /// This function takes the value from BetterError to be explicitly handled by the caller. This
    /// value can be changed, dropped, or passed to other variables. However, it closes the chance of
    /// any more active operations and requires a catch phase in the sequence.
    ///
    /// ### Params:
    /// @f: The closure to consume the value.
    ///
    /// ### Returns:
    /// An instance of Self in `ValConsumed` state
    pub fn then_consume<F>(self, f: F) -> BetterResult<T, K, sealed::ValConsumed>
    where
        F: FnOnce(T),
    {
        match self {
            BetterResult::Val(Some(v)) => {
                f(v);
                BetterResult::<T, K, sealed::ValConsumed>::Val(None)
            }
            BetterResult::Err(Some(e)) => BetterResult::<T, K, sealed::ValConsumed>::Err(Some(e)),
            _ => panic!("Something went terribly wrong here."),
        }
    }

    /// Consumes the value into a then operation that can cast it to a new type.
    ///
    /// It takes the value from BetterError to be explicitly handled and casted by the caller at the
    /// closure. The value can be changed, dropped, or passed to other variables as the new type.
    /// However, it closes the chance of any more active operations and requires a catch phase in
    /// the sequence.
    ///
    /// ### Params:
    /// @f: The closure to consume and cast the value. \
    /// @NewT: The new type of the value, passed as a typed argument.
    ///
    /// ### Returns:
    /// An instance of Self in `ValConsumed` state
    pub fn then_consume_as<F, NewT>(self, f: F) -> BetterResult<NewT, K, sealed::ValConsumed>
    where
        F: FnOnce(T),
    {
        match self {
            BetterResult::Val(Some(v)) => {
                f(v);
                BetterResult::<NewT, K, sealed::ValConsumed>::Val(None)
            }
            BetterResult::Err(Some(e)) => {
                BetterResult::<NewT, K, sealed::ValConsumed>::Err(Some(e))
            }
            _ => panic!("Something went terribly wrong here."),
        }
    }

    /// Catches an error value and consume it on a closure to be handled by the caller.
    ///
    /// If any call on the chain prior to catch returns an error, this function will capture it,
    /// wrap it in a caller defined closure and return an empty [`BetterResult::Err`] to the finally
    /// function. This action will cause any finally operations to skip.
    ///
    /// In case a value is found instead, the result is passed through the chain to the finally
    /// operations to handle it.
    ///
    /// ### Params:
    /// @f: The closure to consume the error value.
    ///
    /// ### Returns:
    /// An instance of Self in `ErrEvaluated` state.
    pub fn catch<F>(self, f: F) -> BetterResult<T, K, sealed::ErrEvaluated>
    where
        F: FnOnce(K),
    {
        match self {
            BetterResult::Err(Some(e)) => {
                f(e);
                BetterResult::<T, K, sealed::ErrEvaluated>::Err(None)
            }
            BetterResult::Val(Some(v)) => BetterResult::<T, K, sealed::ErrEvaluated>::Val(Some(v)),
            _ => panic!("Something went terribly wrong here."),
        }
    }

    /// Catches an error value and consume as another error type it on a closure to be handled by 
    /// the caller.
    ///
    /// If any call on the chain prior to catch returns an error, this function will capture it,
    /// wrap it in a caller defined closure and return an empty [`BetterResult::Err`] to the finally
    /// function. This action will cause any finally operations to skip.
    ///
    /// In case a value is found instead, the result is passed through the chain to the finally
    /// operations to handle it.
    ///
    /// ### Params:
    /// @f: The closure to consume the error value.
    ///
    /// ### Returns:
    /// An instance of Self in `ErrEvaluated` state.
    pub fn catch_as<F, NewK>(self, f: F) -> BetterResult<T, NewK, sealed::ErrEvaluated>
    where
        F: FnOnce(K),
        NewK: std::fmt::Display + std::error::Error,
    {
        match self {
            BetterResult::Err(Some(e)) => {
                f(e);
                BetterResult::<T, NewK, sealed::ErrEvaluated>::Err(None)
            }
            BetterResult::Val(Some(v)) => {
                BetterResult::<T, NewK, sealed::ErrEvaluated>::Val(Some(v))
            }
            _ => panic!("Something went terribly wrong here."),
        }
    }

    /// Catches a mutable reference to the error value to be handled internally on the closure.
    ///
    /// This function don't return any value. It simply passes the error value, if present, to be
    /// handled by the caller inside the closure itself. Therefore, it is consider a breakpoint
    /// method that immediatelly cuts the chain and determines the error resolution.
    ///
    /// ### Params:
    /// @f: The closure to handler the error.
    pub fn catch_contained<F>(&mut self, f: F)
    where
        F: FnOnce(&mut K),
    {
        match self {
            BetterResult::Err(Some(e)) => f(e),
            _ => {}
        }
    }
}

impl<T, K> BetterResult<T, K, sealed::ValConsumed> {
    /// Catches an error value and consume it on a closure to be handled by the caller.
    ///
    /// This is a breakpoint method that activelly requires the error to be either handled, or
    /// returned by the calling method. It does not return any chain operations after catching an
    /// error.
    ///
    /// ### Params:
    /// @f: The closure to consume the error value.
    ///
    pub fn catch<F>(self, f: F)    
    where
        F: FnOnce(K),
    {
        match self {
            BetterResult::Err(Some(e)) => f(e),
            BetterResult::Val(None) => {},
            _ => panic!("Something went terribly wrong here."),
        }
    }
}

impl<T, K> BetterResult<T, K, sealed::ErrEvaluated> {
    /// Optionally returns the value to be consumed by the caller
    ///
    /// If any errors are catch up the chain, the closure provided by the user is skipped and [`None`]
    /// is returned instead. Otherwise, wraps the result value in a closure that will consume it to
    /// the caller variable.
    ///
    /// ### Params:
    /// @f: The closure that will consume and return the value.
    ///
    /// ### Returns:
    /// Optionally a value if no errors were catched.
    pub fn finally<F>(self, f: F) -> Option<T>
    where
        F: FnOnce(T) -> T,
    {
        match self {
            BetterResult::Val(Some(v)) => Some(f(v)),
            BetterResult::Err(None) => None,
            _ => panic!("Something went terribly wrong here."),
        }
    }

    /// Optionally returns the value to be consumed by the caller as a new type.
    ///
    /// If any errors are catch up the chain, the closure provided by the user is skipped and [`None`]
    /// is returned instead. Otherwise, wraps the result value in a closure that will consume it to
    /// the caller variable.
    ///
    /// ### Params:
    /// @f: The closure that will consume and return the value. \
    /// @NewT: The new type of the result value, passed as a typed argument.
    ///
    /// ### Returns:
    /// Optionally a value if no errors were catched.
    pub fn finally_as<F, NewT>(self, f: F) -> Option<NewT>
    where
        F: FnOnce(T) -> NewT,
    {
        match self {
            BetterResult::Val(Some(v)) => Some(f(v)),
            BetterResult::Err(None) => None,
            _ => panic!("Something went terribly wrong here."),
        }
    }
}

/// Expand error enum types into more robust error structures with default messages, declared field
/// replacement at compile time in the message and foreing error casting declarations.
#[macro_export]
macro_rules! better_error {
    (
        $(#[$meta:meta])*
        $vis:vis enum $name:ident {
            $(
                $variant:ident $({ $($field:ident: $fty:ty),* })? => $msg:literal
            ),*
            $(,)?
        }
        $(
            from {
                $($from_variant:ident($from_ty:ty)),*
                $(,)?
            }
        )?
    ) =>{
        $(#[$meta])*
        $vis enum $name {
            $($variant $({ $($field: $fty),* })?),*
            $($(, $from_variant($from_ty))*)?
        }

        impl std::fmt::Display for $name {
            fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
                match self {
                    $(
                        Self::$variant $({ $($field),* })? => write!(f, $msg $(, $($field = $field),*)?),
                    )*
                    $($(
                        Self::$from_variant(inner) => write!(f, "{}", inner),
                    )*)?
                }
            }
        }

        impl std::error::Error for $name {}

        $($(
            impl From<$from_ty> for $name {
                fn from(e: $from_ty) -> Self {
                    $name::$from_variant(e)
                }
            }
        )*)?
    };
}

/// Expands BetterResult into a match expression that either returns the result value to the caller
/// variable, or returns a BetterResult::Err from the caller, kinda like `?` behaves.
#[macro_export]
macro_rules! chain_up {
    ($expr:expr) => {
        match $expr {
            BetterResult::Val(Some(v)) => v,
            BetterResult::Err(Some(e)) => return BetterResult::fail(e),
            _ => panic!("Something went terribly wrong here"),
        }
    };
}

/// ------------------------------------------------------------------------------------
///
/// Unitary tests
///
/// ------------------------------------------------------------------------------------

#[cfg(test)]
mod tests {
    use crate::BetterResult;

    // Template error type one. To be used in testing.
    better_error! {
        #[derive(Debug, PartialEq)]
        pub enum ErrorTypeOne {
            ErrorOneFail {
                exp_no: u32,
                reason: String
            } => "This is a failed example with {exp_no} number: {reason}",
            ErrorOneEmpty => "This is an empty error message",
        }
    }

    // Template error type two. To be used in testing
    better_error! {
        #[derive(Debug, PartialEq)]
        pub enum ErrorTypeTwo {
            _ErrorTwoFail {
                path: String,
                reason: String
            } => "Example fail for path {path}: {reason}",
            ErrorTwoEmpty => "Another empty error",
        }
        from {
            ForeignError(ErrorTypeOne),
        }
    }

    /// A function that invariantly succeeds BetterResult.
    fn function_that_succeeds() -> BetterResult<u32, ErrorTypeOne> {
        BetterResult::succeed(10)
    }

    /// A function that invariantly fails BetterResult
    fn function_that_fails() -> BetterResult<u32, ErrorTypeOne> {
        BetterResult::fail(ErrorTypeOne::ErrorOneFail {
            exp_no: 10,
            reason: "Some reason".into()
        })
    }

    /// A function that invariantly fails BetterResult with ErrorTypeOne nested into ErrorTypeTwo.
    fn nested_fail() -> BetterResult<(), ErrorTypeTwo> {
        let mut err: ErrorTypeOne = ErrorTypeOne::ErrorOneEmpty;
        function_that_fails()
            .catch(|f| err = f);

        BetterResult::fail(ErrorTypeTwo::ForeignError(err))
    }

    /// Tests the pattern matching capabilities of BetterResult enum.
    #[test]
    fn pattern_matching() {
        let res_1 = match function_that_succeeds() {
            BetterResult::Val(_) => true,
            _ => false,
        };

        let res_2 = match function_that_fails() {
            BetterResult::Err(_) => true,
            _ => false,
        };

        assert!(res_1);
        assert!(res_2);
    }

    /// Tests the monadic chain Then -> Catch -> Finally with successful end.
    #[test]
    fn monad_then_catch_finally_successful() {
        let mut some_val: u32 = 50;

        let mut res_ok: Option<bool> = function_that_succeeds()
            .then(|v| some_val += *v)
            .catch(|_| {})
            .finally_as::<_, bool>(|_| true);

        assert_eq!(some_val, 60);
        assert_eq!(res_ok.take(), Some(true));
    }

    /// Tests the monadic chain Then -> Catch -> Finally with failed end.
    #[test]
    fn monad_then_catch_finally_failed() {
        let mut some_val: u32 = 50;
        let mut err_val: Option<ErrorTypeOne> = None;

        let mut res_err: Option<bool> = function_that_fails()
            .then(|v| some_val += *v)
            .catch(|e| err_val = Some(e))
            .finally_as::<_, bool>(|_| true); // wont run regardless.
                              
        assert_eq!(some_val, 50);
        assert_eq!(res_err.take(), None);
        assert!(err_val.is_some());
        assert_eq!(err_val.take(), Some(ErrorTypeOne::ErrorOneFail {exp_no: 10, reason: "Some reason".into()}))
    }

    /// Tests the monadic chain ThenConsume -> Catch with successful end.
    #[test]
    fn monad_then_consume_catch_successful() {
        let mut some_val: u32 = 50;

        function_that_succeeds()
            .then_consume(|v| some_val = v)
            .catch(|_| {});

        assert_eq!(some_val, 10);
    }

    /// Tests the monadic chain ThenConsumeAs -> Catch with successful end.
    #[test]
    fn monad_then_consume_as_catch_successful() {
        let mut some_val: f64 = 50.63;

        function_that_succeeds()
            .then_consume_as::<_, f64>(|v| {
                let v_f64 = v as f64;
                some_val *= v_f64
            })
            .catch(|_| {});

        assert_eq!(some_val, 50.63 * 10.00);
    }

    /// Tests the monadic chain ThenConsume -> Catch with failed end.
    #[test]
    fn monad_then_consume_catch_fail() {
        let mut some_val: u32 = 50;
        let mut err_val: Option<ErrorTypeOne> = None;

        function_that_fails()
            .then_consume(|v| some_val = v)
            .catch(|e| err_val = Some(e));

        assert_eq!(some_val, 50);
        assert!(err_val.is_some());
        assert_eq!(err_val.take(), Some(ErrorTypeOne::ErrorOneFail {exp_no: 10, reason: "Some reason".into()}))
    }

    /// Tests the monadic chain ThenConsumeAs -> Catch with failed end.
    #[test]
    fn monad_then_consume_as_catch_fail() {
        let mut some_val: f64 = 50.63;
        let mut err_val: Option<ErrorTypeOne> = None;

        function_that_fails()
            .then_consume_as::<_, f64>(|v| {
                let v_f64 = v as f64;
                some_val *= v_f64
            })
            .catch(|e| err_val = Some(e));

        assert_eq!(some_val, 50.63);
        assert!(err_val.is_some());
        assert_eq!(err_val.take(), Some(ErrorTypeOne::ErrorOneFail {exp_no: 10, reason: "Some reason".into()}))
    }

    /// Tests the chained monad ThenAs -> ThenAs -> Catch -> Finally with successful end.
    #[test]
    fn monad_chained_then_as_catch_finally_successful() {
        fn join(val_1: u32, val_2: u32) -> BetterResult<u32, ErrorTypeOne> {
            let val = format!("{val_1}{val_2}");
            return BetterResult::succeed(val.parse().unwrap())
        }

        fn transform<'a>(val: u32) -> BetterResult<Vec<u32>, ErrorTypeOne> {
            let final_val_list: Vec<u32> = val
                .to_string()
                .chars()
                .map(|c| c.to_digit(10).unwrap())
                .collect();


            return BetterResult::succeed(final_val_list)
        }

        fn aggregate(val_list: Vec<u32>) -> BetterResult<u32, ErrorTypeOne> {
            let mut final_val: u32 = 0;
            for val in val_list {
                final_val += val
            }

            BetterResult::succeed(final_val)
        }

        let val_1 = 52;
        let val_2 = 2481;

        let mut res = join(val_1, val_2)
            .then_as::<_, Vec<u32>>(|v| transform(*v))
            .then_as::<_, u32>(|v_list| aggregate(v_list.clone()))
            .catch(|_| {})
            .finally(|v| v);

        assert!(res.is_some());
        assert_eq!(res.take(), Some(22));
    }

    /// Tests the chained monad ThenAs -> ThenAs -> Catch -> Finally with failed end.
    #[test]
    fn monad_chained_then_as_catch_finally_fail() {
        fn join(val_1: u32, val_2: u32) -> BetterResult<u32, ErrorTypeOne> {
            let val = format!("{val_1}{val_2}");
            return BetterResult::succeed(val.parse().unwrap())
        }

        fn transform<'a>(val: u32) -> BetterResult<Vec<u32>, ErrorTypeOne> {
            let _final_val_list: Vec<u32> = val
                .to_string()
                .chars()
                .map(|c| c.to_digit(10).unwrap())
                .collect();


            return BetterResult::fail(ErrorTypeOne::ErrorOneEmpty)
        }

        fn aggregate(val_list: Vec<u32>) -> BetterResult<u32, ErrorTypeOne> {
            let mut final_val: u32 = 0;
            for val in val_list {
                final_val += val
            }

            BetterResult::succeed(final_val)
        }

        let val_1 = 52;
        let val_2 = 2481;
        let mut err_val: Option<ErrorTypeOne> = None;

        let res = join(val_1, val_2)
            .then_as::<_, Vec<u32>>(|v| transform(*v))
            .then_as::<_, u32>(|v_list| aggregate(v_list.clone()))
            .catch(|e| err_val = Some(e))
            .finally(|v| v);

        assert!(res.is_none());
        assert!(err_val.is_some());
        assert_eq!(err_val, Some(ErrorTypeOne::ErrorOneEmpty));
    }

    /// Tests if chain_up! successfully assigning the value.
    #[test]
    fn chain_up_successfully_assign() {
        fn chain_up_assign() -> BetterResult<(), ErrorTypeOne> {
            let _val = chain_up!(function_that_succeeds());
            BetterResult::succeed(())
        }
        
        let err_val = chain_up_assign();

        assert!(err_val.is_ok());
    }

    /// Tests if chain_up! successfully escalates an error.
    #[test]
    fn chain_up_escalate_error() {
        fn chain_up_escalate() -> BetterResult<(), ErrorTypeOne> {
            let _val = chain_up!(function_that_fails());
            BetterResult::succeed(())
        }

        let err_val = chain_up_escalate();

        assert!(err_val.is_err());
    }

    /// Test if can be converged a from Result value.
    #[test]
    fn converge_from_result() {
        let val: Result<u32, ErrorTypeTwo> = Ok(1);

        let res = BetterResult::converge(val)
            .catch(|_| {})
            .finally(|v| v);

        assert_eq!(res, Some(1));
    }

    /// Test if can be diverged into a Result value.
    #[test]
    fn diverge_to_result() {
        let err: BetterResult<u32, ErrorTypeTwo> = BetterResult::fail(ErrorTypeTwo::ErrorTwoEmpty);

        let res = err.diverge();

        assert!(res.is_err());
        assert_eq!(res, Err(ErrorTypeTwo::ErrorTwoEmpty));
    }

    /// Tests nested fail results.
    #[test]
    fn catch_nested_fail() {
        let mut res: Option<ErrorTypeTwo> = None;

        nested_fail()
            .then_consume(|_| {})
            .catch(|e| res = Some(e));

        assert!(res.is_some());
        assert_eq!(res, Some(ErrorTypeTwo::ForeignError(ErrorTypeOne::ErrorOneFail {
            exp_no: 10,
            reason: "Some reason".into()
        })));
    }
}
