anystack 0.6.0-alpha.3

Flexible and comprehensive error handling.
Documentation
#![expect(deprecated, reason = "We use `Context` to maintain compatibility")]

use core::{error::Error, fmt};

use crate::{Context, IntoReport, Report};

#[deprecated(
    note = "Use `core::result::Result<T, Report<C>>` or `anystack::ResultStack` instead",
    since = "0.6.0"
)]
pub type Result<T, C = dyn Error> = core::result::Result<T, Report<C>>;

/// `Result<T, Error>`
///
/// This is a reasonable return type to use throughout your application.
///
/// `anystack::ResultStack` may be used with one *or* two type parameters.
///
/// ```rust
/// # const _IGNORE: &str = stringify! {
/// fn demo1() -> anystack::ResultStack<T> {...}
///            // ^ equivalent to:
///            // std::result::Result<T, anystack::Report<dyn core::error::Error>>
///
/// fn demo2() -> anystack::ResultStack<T, OtherError> {...}
///            // ^ equivalent to:
///            // std::result::Result<T, anystack::Report<OtherError>>
/// # };
/// ```
///
/// # Example
///
/// ```no_run
/// # pub trait Deserialize {}
/// #
/// # mod serde_json {
/// #     use super::Deserialize;
/// #     use std::io;
/// #
/// #     pub fn from_str<T: Deserialize>(_json: &str) -> io::Result<T> {
/// #         unimplemented!()
/// #     }
/// # }
/// #
/// # #[derive(Debug)]
/// # struct ClusterMap;
/// #
/// # impl Deserialize for ClusterMap {}
/// #
/// use anystack::ResultStack;
///
/// fn main() -> ResultStack<()> {
///     let config = std::fs::read_to_string("cluster.json")?;
///     let map: ClusterMap = serde_json::from_str(&config)?;
///     println!("cluster info: {:#?}", map);
///     Ok(())
/// }
/// ```
pub type ResultStack<T, C = dyn Error> = core::result::Result<T, Report<C>>;

/// Extension trait for [`Result`][core::result::Result] to provide context information on
/// [`Report`]s.
pub trait ResultExt {
    /// The [`Context`] type of the [`Result`].
    type Context: ?Sized;

    /// Type of the [`Ok`] value in the [`Result`]
    type Ok;

    /// Adds a new attachment to the [`Report`] inside the [`Result`].
    ///
    /// Applies [`Report::attach`] on the [`Err`] variant, refer to it for more information.
    fn attach<A>(self, attachment: A) -> core::result::Result<Self::Ok, Report<Self::Context>>
    where
        A: Send + Sync + 'static;

    /// Lazily adds a new attachment to the [`Report`] inside the [`Result`].
    ///
    /// Applies [`Report::attach`] on the [`Err`] variant, refer to it for more information.
    fn attach_lazy<A, F>(
        self,
        attachment: F,
    ) -> core::result::Result<Self::Ok, Report<Self::Context>>
    where
        A: Send + Sync + 'static,
        F: FnOnce() -> A;

    /// Adds a new printable attachment to the [`Report`] inside the [`Result`].
    ///
    /// Applies [`Report::attach_printable`] on the [`Err`] variant, refer to it for more
    /// information.
    fn attach_printable<A>(
        self,
        attachment: A,
    ) -> core::result::Result<Self::Ok, Report<Self::Context>>
    where
        A: fmt::Display + fmt::Debug + Send + Sync + 'static;

    /// Lazily adds a new printable attachment to the [`Report`] inside the [`Result`].
    ///
    /// Applies [`Report::attach_printable`] on the [`Err`] variant, refer to it for more
    /// information.
    fn attach_printable_lazy<A, F>(
        self,
        attachment: F,
    ) -> core::result::Result<Self::Ok, Report<Self::Context>>
    where
        A: fmt::Display + fmt::Debug + Send + Sync + 'static,
        F: FnOnce() -> A;

    /// Changes the context of the [`Report`] inside the [`Result`].
    ///
    /// Applies [`Report::change_context`] on the [`Err`] variant, refer to it for more information.
    fn change_context<C>(self, context: C) -> core::result::Result<Self::Ok, Report<C>>
    where
        C: Context;

    /// Lazily changes the context of the [`Report`] inside the [`Result`].
    ///
    /// Applies [`Report::change_context`] on the [`Err`] variant, refer to it for more information.
    fn change_context_lazy<C, F>(self, context: F) -> core::result::Result<Self::Ok, Report<C>>
    where
        C: Context,
        F: FnOnce() -> C;
}

impl<T, E> ResultExt for core::result::Result<T, E>
where
    E: IntoReport,
{
    type Context = E::Context;
    type Ok = T;

    #[track_caller]
    fn attach<A>(self, attachment: A) -> core::result::Result<T, Report<E::Context>>
    where
        A: Send + Sync + 'static,
    {
        match self {
            Ok(value) => Ok(value),
            Err(error) => Err(error.into_report().attach(attachment)),
        }
    }

    #[track_caller]
    fn attach_lazy<A, F>(self, attachment: F) -> core::result::Result<T, Report<E::Context>>
    where
        A: Send + Sync + 'static,
        F: FnOnce() -> A,
    {
        match self {
            Ok(value) => Ok(value),
            Err(error) => Err(error.into_report().attach(attachment())),
        }
    }

    #[track_caller]
    fn attach_printable<A>(self, attachment: A) -> core::result::Result<T, Report<E::Context>>
    where
        A: fmt::Display + fmt::Debug + Send + Sync + 'static,
    {
        match self {
            Ok(value) => Ok(value),
            Err(error) => Err(error.into_report().attach_printable(attachment)),
        }
    }

    #[track_caller]
    fn attach_printable_lazy<A, F>(
        self,
        attachment: F,
    ) -> core::result::Result<T, Report<E::Context>>
    where
        A: fmt::Display + fmt::Debug + Send + Sync + 'static,
        F: FnOnce() -> A,
    {
        match self {
            Ok(value) => Ok(value),
            Err(error) => Err(error.into_report().attach_printable(attachment())),
        }
    }

    #[track_caller]
    fn change_context<C>(self, context: C) -> core::result::Result<T, Report<C>>
    where
        C: Context,
    {
        match self {
            Ok(value) => Ok(value),
            Err(error) => Err(error.into_report().change_context(context)),
        }
    }

    #[track_caller]
    fn change_context_lazy<C, F>(self, context: F) -> core::result::Result<T, Report<C>>
    where
        C: Context,
        F: FnOnce() -> C,
    {
        match self {
            Ok(value) => Ok(value),
            Err(error) => Err(error.into_report().change_context(context())),
        }
    }
}