use rootcause_internals::handlers;

use crate::{
    IntoReport, Report,
    into_report::IntoReportCollection,
    markers::{Local, Mutable, ObjectMarker, SendSync},
};

mod sealed {
    pub trait Sealed {}
    impl<A, E> Sealed for Result<A, E> {}
}

/// Extension trait for `Result` that provides error handling and reporting
/// functionality.
///
/// This trait adds methods to `Result` types that allow you to convert errors
/// into [`Report`]s with additional context and attachments. It provides both
/// thread-safe (`Send + Sync`) and local-only variants of each method.
///
/// The methods in this trait fall into several categories:
///
/// - **Converting to reports**: [`into_report`](ResultExt::into_report)
///   converts the error into a [`Report`]
/// - **Adding context**: [`context`](ResultExt::context),
///   [`context_with`](ResultExt::context_with), and variants add a new context
///   layer to the error
/// - **Adding attachments**: [`attach`](ResultExt::attach),
///   [`attach_with`](ResultExt::attach_with), and variants add supplementary
///   data to the error
///
/// Each method has a `local_*` variant for working with types that are not
/// `Send + Sync`.
pub trait ResultExt<V, E>: sealed::Sealed {
    /// Converts the error into a [`Report`].
    ///
    /// If the result is `Ok`, returns the value unchanged. If the result is
    /// `Err`, converts the error into a [`Report`].
    ///
    /// See also [`local_into_report`](ResultExt::local_into_report) for a
    /// non-thread-safe version that works with types that are not
    /// `Send + Sync`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// fn read_config() -> Result<String, io::Error> {
    ///     std::fs::read_to_string("config.toml")
    /// }
    ///
    /// let result: Result<String, Report<io::Error>> = read_config().into_report();
    /// ```
    #[track_caller]
    fn into_report(self) -> Result<V, Report<E::Context, E::Ownership, SendSync>>
    where
        E: IntoReport<SendSync>;

    /// Converts the error into a new [`Report`] using the provided context. The
    /// current error is set as a child of the new [`Report`].
    ///
    /// If the result is `Ok`, returns the value unchanged. If the result is
    /// `Err`, converts the error into a report collection and adds the
    /// provided context. The context becomes the primary error message,
    /// with the original error becoming part of the error chain.
    ///
    /// This method also works when the original error is a
    /// [`ReportCollection`], in which case all errors in that collection
    /// are set as children of the new [`Report`].
    ///
    /// See also [`local_context`](ResultExt::local_context) for a
    /// non-thread-safe version that works with types that are not
    /// `Send + Sync`.
    ///
    /// [`ReportCollection`]: crate::report_collection::ReportCollection
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// fn fetch_data(path: &str) -> Result<Vec<u8>, io::Error> {
    ///     std::fs::read(path)
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<&str>> =
    ///     fetch_data("user_data.bz2").context("Failed to fetch user data");
    /// ```
    #[track_caller]
    fn context<C>(self, context: C) -> Result<V, Report<C, Mutable, SendSync>>
    where
        E: IntoReportCollection<SendSync>,
        C: ObjectMarker + Send + Sync + core::fmt::Display + core::fmt::Debug;

    /// Converts the error into a new [`Report`] using context generated by the
    /// provided closure. The current error is set as a child of the new
    /// [`Report`].
    ///
    /// This is similar to [`context`](ResultExt::context), but the context is
    /// computed lazily using a closure. This can be useful when computing
    /// the context is expensive, as the closure will only be called if an
    /// error actually occurs.
    ///
    /// This method also works when the original error is a
    /// [`ReportCollection`], in which case all errors in that collection
    /// are set as children of the new [`Report`].
    ///
    /// See also [`local_context_with`](ResultExt::local_context_with) for a
    /// non-thread-safe version that works with types that are not
    /// `Send + Sync`.
    ///
    /// [`ReportCollection`]: crate::report_collection::ReportCollection
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// fn get_timestamp() -> String {
    ///     "12:34:56".to_string()
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<String>> =
    ///     std::fs::read("user_data.bz2").context_with(|| format!("Failed at {}", get_timestamp()));
    /// ```
    #[track_caller]
    fn context_with<C, F>(self, context: F) -> Result<V, Report<C, Mutable, SendSync>>
    where
        E: IntoReportCollection<SendSync>,
        F: FnOnce() -> C,
        C: ObjectMarker + Send + Sync + core::fmt::Display + core::fmt::Debug;

    /// Converts the error into a new [`Report`] using the provided context.
    /// The current error is set as a child of the new [`Report`].
    ///
    /// This is similar to [`context`](ResultExt::context), but uses a custom
    /// [`ContextHandler`] to control how the context is formatted and
    /// displayed.
    ///
    /// This method also works when the original error is a
    /// [`ReportCollection`], in which case all errors in that collection
    /// are set as children of the new [`Report`].
    ///
    /// See also [`local_context_custom`](ResultExt::local_context_custom) for a
    /// non-thread-safe version that works with types that are not
    /// `Send + Sync`.
    ///
    /// [`ContextHandler`]: handlers::ContextHandler
    /// [`ReportCollection`]: crate::report_collection::ReportCollection
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// enum ErrorContext {
    ///     DeserializationError,
    ///     IoError,
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<ErrorContext>> =
    ///     std::fs::read("user_data.bz2").context_custom::<handlers::Debug, _>(ErrorContext::IoError);
    /// ```
    #[track_caller]
    fn context_custom<H, C>(self, context: C) -> Result<V, Report<C, Mutable, SendSync>>
    where
        E: IntoReportCollection<SendSync>,
        C: ObjectMarker + Send + Sync,
        H: handlers::ContextHandler<C>;

    /// Converts the error into a new [`Report`] using context generated by the
    /// provided closure. The current error is set as a child of the new
    /// [`Report`].
    ///
    /// Combines the lazy evaluation of
    /// [`context_with`](ResultExt::context_with) with the custom
    /// handler capabilities of [`context_custom`](ResultExt::context_custom).
    ///
    /// This method also works when the original error is a
    /// [`ReportCollection`], in which case all errors in that collection
    /// are set as children of the new [`Report`].
    ///
    /// See also [`local_context_custom_with`](ResultExt::local_context_custom_with)
    /// for a non-thread-safe version that works with types that are not
    /// `Send + Sync`.
    ///
    /// [`ReportCollection`]: crate::report_collection::ReportCollection
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// struct ErrorContext {
    ///     timestamp: String,
    ///     operation: String,
    /// }
    ///
    /// fn expensive_computation() -> ErrorContext {
    ///     ErrorContext {
    ///         timestamp: "12:34:56".to_string(),
    ///         operation: "file_read".to_string(),
    ///     }
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<ErrorContext>> =
    ///     std::fs::read("user_data.bz2")
    ///         .context_custom_with::<handlers::Debug, _, _>(expensive_computation);
    /// ```
    #[track_caller]
    fn context_custom_with<H, C, F>(self, context: F) -> Result<V, Report<C, Mutable, SendSync>>
    where
        E: IntoReportCollection<SendSync>,
        F: FnOnce() -> C,
        C: ObjectMarker + Send + Sync,
        H: handlers::ContextHandler<C>;

    /// Converts the error into a [`Report`] and adds the provided attachment to
    /// the [`Report`].
    ///
    /// If the result is `Ok`, returns the value unchanged. If the result is
    /// `Err`, converts the error into a [`Report`] and attaches the
    /// provided data as an attachment. Attachments provide additional
    /// context without changing the primary error message.
    ///
    /// See also [`local_attach`](ResultExt::local_attach) for a non-thread-safe
    /// version that works with types that are not `Send + Sync`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// let result: Result<Vec<u8>, Report<io::Error>> =
    ///     std::fs::read("user_data.bz2").attach("while reading user_data.bz2");
    /// ```
    #[track_caller]
    fn attach<A>(self, attachment: A) -> Result<V, Report<E::Context, Mutable, SendSync>>
    where
        E: IntoReport<SendSync, Ownership = Mutable>,
        A: ObjectMarker + Send + Sync + core::fmt::Display + core::fmt::Debug;

    /// Converts the error into a [`Report`] and adds an attachment generated by
    /// the provided closure to the [`Report`].
    ///
    /// This is similar to [`attach`](ResultExt::attach), but the attachment is
    /// computed lazily using a closure. This can be useful when computing
    /// the attachment is expensive, as the closure will only be called if
    /// an error actually occurs.
    ///
    /// See also [`local_attach_with`](ResultExt::local_attach_with) for a
    /// non-thread-safe version that works with types that are not
    /// `Send + Sync`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// fn get_debug_info() -> String {
    ///     "complex computation result".to_string()
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<io::Error>> =
    ///     std::fs::read("user_data.bz2").attach_with(|| format!("debug info: {}", get_debug_info()));
    /// ```
    #[track_caller]
    fn attach_with<A, F>(self, attachment: F) -> Result<V, Report<E::Context, Mutable, SendSync>>
    where
        E: IntoReport<SendSync, Ownership = Mutable>,
        F: FnOnce() -> A,
        A: ObjectMarker + Send + Sync + core::fmt::Display + core::fmt::Debug;

    /// Converts the error into a [`Report`] and adds the provided attachment to
    /// the [`Report`].
    ///
    /// This is similar to [`attach`](ResultExt::attach), but uses a custom
    /// [`AttachmentHandler`] to control how the attachment is formatted and
    /// displayed.
    ///
    /// See also [`local_attach_custom`](ResultExt::local_attach_custom) for a
    /// non-thread-safe version that works with types that are not
    /// `Send + Sync`.
    ///
    /// [`AttachmentHandler`]: handlers::AttachmentHandler
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// struct RequestMetadata {
    ///     request_id: u64,
    ///     user_id: u64,
    /// }
    ///
    /// let metadata = RequestMetadata {
    ///     request_id: 12345,
    ///     user_id: 67890,
    /// };
    ///
    /// let result: Result<Vec<u8>, Report<io::Error>> =
    ///     std::fs::read("user_data.bz2").attach_custom::<handlers::Debug, _>(metadata);
    /// ```
    #[track_caller]
    fn attach_custom<H, A>(self, attachment: A) -> Result<V, Report<E::Context, Mutable, SendSync>>
    where
        E: IntoReport<SendSync, Ownership = Mutable>,
        A: ObjectMarker + Send + Sync,
        H: handlers::AttachmentHandler<A>;

    /// Converts the error into a [`Report`] and adds an attachment generated by
    /// the provided closure to the [`Report`].
    ///
    /// Combines the lazy evaluation of [`attach_with`](ResultExt::attach_with)
    /// with the custom handler capabilities of
    /// [`attach_custom`](ResultExt::attach_custom).
    ///
    /// See also [`local_attach_custom_with`](ResultExt::local_attach_custom_with)
    /// for a non-thread-safe version that works with types that are not
    /// `Send + Sync`.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::io;
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// struct RequestMetadata {
    ///     request_id: u64,
    ///     timestamp: String,
    /// }
    ///
    /// fn expensive_computation() -> RequestMetadata {
    ///     RequestMetadata {
    ///         request_id: 12345,
    ///         timestamp: "12:34:56".to_string(),
    ///     }
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<io::Error>> = std::fs::read("user_data.bz2")
    ///     .attach_custom_with::<handlers::Debug, _, _>(expensive_computation);
    /// ```
    #[track_caller]
    fn attach_custom_with<H, A, F>(
        self,
        attachment: F,
    ) -> Result<V, Report<E::Context, Mutable, SendSync>>
    where
        E: IntoReport<SendSync, Ownership = Mutable>,
        F: FnOnce() -> A,
        A: ObjectMarker + Send + Sync,
        H: handlers::AttachmentHandler<A>;

    /// Converts the error into a local (non-thread-safe) [`Report`].
    ///
    /// If the result is `Ok`, returns the value unchanged. If the result is
    /// `Err`, converts the error into a [`Report`] that can contain types
    /// that are not `Send + Sync`.
    ///
    /// This allows you to use rootcause with error types that contain
    /// thread-local data like [`Rc`](std::rc::Rc) or other `!Send` types.
    ///
    /// See also [`into_report`](ResultExt::into_report) for a thread-safe
    /// version that returns a [`Report`] that can be sent across thread
    /// boundaries.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{fmt, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// // This error is neither Send nor Sync
    /// #[derive(Debug)]
    /// struct MyError(Rc<String>);
    ///
    /// impl fmt::Display for MyError {
    ///     fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
    ///         write!(f, "{}", self.0)
    ///     }
    /// }
    ///
    /// impl std::error::Error for MyError {}
    ///
    /// fn some_function() -> Result<String, MyError> {
    ///     Err(MyError(Rc::new("error".to_string())))
    /// }
    ///
    /// let result: Result<String, Report<MyError, _, markers::Local>> =
    ///     some_function().local_into_report();
    /// ```
    #[track_caller]
    fn local_into_report(self) -> Result<V, Report<E::Context, E::Ownership, Local>>
    where
        E: IntoReport<Local>;

    /// Converts the error into a new local (non-thread-safe) [`Report`] using
    /// the provided context. The current error is set as a child of the new
    /// [`Report`].
    ///
    /// If the result is `Ok`, returns the value unchanged. If the result is
    /// `Err`, converts the error into a report collection and adds the
    /// provided context. The context becomes the primary error message,
    /// with the original error becoming part of the error chain.
    ///
    /// This method also works when the original error is a
    /// [`ReportCollection`], in which case all errors in that collection
    /// are set as children of the new [`Report`].
    ///
    /// See also [`context`](ResultExt::context) for a thread-safe version that
    /// returns a [`Report`] that can be sent across thread boundaries.
    ///
    /// [`ReportCollection`]: crate::report_collection::ReportCollection
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{io, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// fn fetch_data(path: &str) -> Result<Vec<u8>, io::Error> {
    ///     std::fs::read(path)
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<Rc<&str>, _, markers::Local>> =
    ///     fetch_data("user_data.bz2").local_context(Rc::from("Failed to fetch user data"));
    /// ```
    #[track_caller]
    fn local_context<C>(self, context: C) -> Result<V, Report<C, Mutable, Local>>
    where
        E: IntoReportCollection<Local>,
        C: ObjectMarker + core::fmt::Display + core::fmt::Debug;

    /// Converts the error into a new local (non-thread-safe) [`Report`] using
    /// context generated by the provided closure. The current error is set
    /// as a child of the new [`Report`].
    ///
    /// This is similar to [`local_context`](ResultExt::local_context), but the
    /// context is computed lazily using a closure. This can be useful when
    /// computing the context is expensive, as the closure will only be
    /// called if an error actually occurs.
    ///
    /// This method also works when the original error is a
    /// [`ReportCollection`], in which case all errors in that collection
    /// are set as children of the new [`Report`].
    ///
    /// See also [`context_with`](ResultExt::context_with) for a thread-safe
    /// version that returns a [`Report`] that can be sent across thread
    /// boundaries.
    ///
    /// [`ReportCollection`]: crate::report_collection::ReportCollection
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{io, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// struct ContextData {
    ///     timestamp: Rc<String>,
    ///     operation: String,
    /// }
    ///
    /// impl std::fmt::Display for ContextData {
    ///     fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
    ///         write!(f, "{}: {}", self.timestamp, self.operation)
    ///     }
    /// }
    ///
    /// fn expensive_computation() -> ContextData {
    ///     // Expensive computation that builds context data
    ///     ContextData {
    ///         timestamp: Rc::new("12:34:56".to_string()),
    ///         operation: "file_read".to_string(),
    ///     }
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<ContextData, _, markers::Local>> =
    ///     std::fs::read("user_data.bz2").local_context_with(expensive_computation);
    /// ```
    #[track_caller]
    fn local_context_with<C, F>(self, context: F) -> Result<V, Report<C, Mutable, Local>>
    where
        E: IntoReportCollection<Local>,
        F: FnOnce() -> C,
        C: ObjectMarker + core::fmt::Display + core::fmt::Debug;

    /// Converts the error into a new local (non-thread-safe) [`Report`] using
    /// the provided context. The current error is set as a child of the new
    /// [`Report`].
    ///
    /// This is similar to [`local_context`](ResultExt::local_context), but uses
    /// a custom [`ContextHandler`] to control how the context is formatted
    /// and displayed.
    ///
    /// This method also works when the original error is a
    /// [`ReportCollection`], in which case all errors in that collection
    /// are set as children of the new [`Report`].
    ///
    /// See also [`context_custom`](ResultExt::context_custom) for a thread-safe
    /// version that returns a [`Report`] that can be sent across thread
    /// boundaries.
    ///
    /// [`ContextHandler`]: handlers::ContextHandler
    /// [`ReportCollection`]: crate::report_collection::ReportCollection
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{io, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// enum ErrorContext {
    ///     DeserializationError,
    ///     IoError,
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<Rc<ErrorContext>, _, markers::Local>> =
    ///     std::fs::read("user_data.bz2")
    ///         .local_context_custom::<handlers::Debug, _>(Rc::new(ErrorContext::IoError));
    /// ```
    #[track_caller]
    fn local_context_custom<H, C>(self, context: C) -> Result<V, Report<C, Mutable, Local>>
    where
        E: IntoReportCollection<Local>,
        C: ObjectMarker,
        H: handlers::ContextHandler<C>;

    /// Converts the error into a new local (non-thread-safe) [`Report`] using
    /// context generated by the provided closure. The current error is set
    /// as a child of the new [`Report`].
    ///
    /// Combines the lazy evaluation of
    /// [`local_context_with`](ResultExt::local_context_with) with the custom
    /// handler capabilities of
    /// [`local_context_custom`](ResultExt::local_context_custom).
    ///
    /// This method also works when the original error is a
    /// [`ReportCollection`], in which case all errors in that collection
    /// are set as children of the new [`Report`].
    ///
    /// See also [`context_custom_with`](ResultExt::context_custom_with) for a
    /// thread-safe version that returns a [`Report`] that can be sent
    /// across thread boundaries.
    ///
    /// [`ReportCollection`]: crate::report_collection::ReportCollection
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{io, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// struct ErrorContext {
    ///     timestamp: Rc<String>,
    ///     operation: String,
    /// }
    ///
    /// fn expensive_computation() -> Rc<ErrorContext> {
    ///     Rc::new(ErrorContext {
    ///         timestamp: Rc::new("12:34:56".to_string()),
    ///         operation: "file_read".to_string(),
    ///     })
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<Rc<ErrorContext>, _, markers::Local>> =
    ///     std::fs::read("user_data.bz2")
    ///         .local_context_custom_with::<handlers::Debug, _, _>(expensive_computation);
    /// ```
    #[track_caller]
    fn local_context_custom_with<H, C, F>(self, context: F) -> Result<V, Report<C, Mutable, Local>>
    where
        E: IntoReportCollection<Local>,
        F: FnOnce() -> C,
        C: ObjectMarker,
        H: handlers::ContextHandler<C>;

    /// Converts the error into a local (non-thread-safe) [`Report`] and adds
    /// the provided attachment to the [`Report`].
    ///
    /// If the result is `Ok`, returns the value unchanged. If the result is
    /// `Err`, converts the error into a [`Report`] and attaches the
    /// provided data as an attachment. Attachments provide additional
    /// context without changing the primary error message.
    ///
    /// See also [`attach`](ResultExt::attach) for a thread-safe version that
    /// returns a [`Report`] that can be sent across thread boundaries.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{io, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// // In this case it might make more sense to use `local_attach_with`, but
    /// // you can assume that the attachment is pre-computed.
    /// let attachment: Rc<&str> = Rc::from("input: invalid_data");
    /// let result: Result<Vec<u8>, Report<io::Error, _, markers::Local>> =
    ///     std::fs::read("user_data.bz2").local_attach(attachment);
    /// ```
    #[track_caller]
    fn local_attach<A>(self, attachment: A) -> Result<V, Report<E::Context, Mutable, Local>>
    where
        E: IntoReport<Local, Ownership = Mutable>,
        A: ObjectMarker + core::fmt::Display + core::fmt::Debug;

    /// Converts the error into a local (non-thread-safe) [`Report`] and adds an
    /// attachment generated by the provided closure to the [`Report`].
    ///
    /// This is similar to [`local_attach`](ResultExt::local_attach), but the
    /// attachment is computed lazily using a closure. This can be useful
    /// when computing the attachment is expensive, as the closure will only
    /// be called if an error actually occurs.
    ///
    /// See also [`attach_with`](ResultExt::attach_with) for a thread-safe
    /// version that returns a [`Report`] that can be sent across thread
    /// boundaries.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{io, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// fn get_debug_info() -> String {
    ///     "complex computation result".to_string()
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<io::Error, _, markers::Local>> =
    ///     std::fs::read("user_data.bz2")
    ///         .local_attach_with(|| Rc::new(format!("debug info: {}", get_debug_info())));
    /// ```
    #[track_caller]
    fn local_attach_with<A, F>(
        self,
        attachment: F,
    ) -> Result<V, Report<E::Context, Mutable, Local>>
    where
        E: IntoReport<Local, Ownership = Mutable>,
        F: FnOnce() -> A,
        A: ObjectMarker + core::fmt::Display + core::fmt::Debug;

    /// Converts the error into a local (non-thread-safe) [`Report`] and adds
    /// the provided attachment to the [`Report`].
    ///
    /// This is similar to [`local_attach`](ResultExt::local_attach), but uses a
    /// custom [`AttachmentHandler`] to control how the attachment is
    /// formatted and displayed.
    ///
    /// See also [`attach_custom`](ResultExt::attach_custom) for a thread-safe
    /// version that returns a [`Report`] that can be sent across thread
    /// boundaries.
    ///
    /// [`AttachmentHandler`]: handlers::AttachmentHandler
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{io, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// struct RequestMetadata {
    ///     request_id: Rc<u64>,
    ///     user_id: u64,
    /// }
    ///
    /// // In this case it might make more sense to use `local_attach_custom_with`, but
    /// // you can assume that the attachment is pre-computed.
    /// let metadata: Rc<RequestMetadata> = Rc::new(RequestMetadata {
    ///     request_id: Rc::new(12345),
    ///     user_id: 67890,
    /// });
    ///
    /// let result: Result<Vec<u8>, Report<io::Error, _, markers::Local>> =
    ///     std::fs::read("user_data.bz2").local_attach_custom::<handlers::Debug, _>(metadata);
    /// ```
    #[track_caller]
    fn local_attach_custom<H, A>(
        self,
        attachment: A,
    ) -> Result<V, Report<E::Context, Mutable, Local>>
    where
        E: IntoReport<Local, Ownership = Mutable>,
        A: ObjectMarker,
        H: handlers::AttachmentHandler<A>;

    /// Converts the error into a local (non-thread-safe) [`Report`] and adds an
    /// attachment generated by the provided closure to the [`Report`].
    ///
    /// Combines the lazy evaluation of
    /// [`local_attach_with`](ResultExt::local_attach_with) with the custom
    /// handler capabilities of
    /// [`local_attach_custom`](ResultExt::local_attach_custom).
    ///
    /// See also [`attach_custom_with`](ResultExt::attach_custom_with) for a
    /// thread-safe version that returns a [`Report`] that can be sent
    /// across thread boundaries.
    ///
    /// # Examples
    ///
    /// ```
    /// use std::{io, rc::Rc};
    ///
    /// use rootcause::prelude::*;
    ///
    /// #[derive(Debug)]
    /// struct RequestMetadata {
    ///     request_id: Rc<u64>,
    ///     timestamp: String,
    /// }
    ///
    /// fn expensive_computation() -> RequestMetadata {
    ///     RequestMetadata {
    ///         request_id: Rc::new(12345),
    ///         timestamp: "12:34:56".to_string(),
    ///     }
    /// }
    ///
    /// let result: Result<Vec<u8>, Report<io::Error, _, markers::Local>> =
    ///     std::fs::read("user_data.bz2")
    ///         .local_attach_custom_with::<handlers::Debug, _, _>(|| Rc::new(expensive_computation()));
    /// ```
    #[track_caller]
    fn local_attach_custom_with<H, A, F>(
        self,
        attachment: F,
    ) -> Result<V, Report<E::Context, Mutable, Local>>
    where
        E: IntoReport<Local, Ownership = Mutable>,
        F: FnOnce() -> A,
        A: ObjectMarker,
        H: handlers::AttachmentHandler<A>;
}

impl<V, E> ResultExt<V, E> for Result<V, E> {
    #[inline(always)]
    fn into_report(self) -> Result<V, Report<E::Context, E::Ownership, SendSync>>
    where
        E: IntoReport<SendSync>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report()),
        }
    }

    #[inline(always)]
    fn context<C>(self, context: C) -> Result<V, Report<C, Mutable, SendSync>>
    where
        E: IntoReportCollection<SendSync>,
        C: ObjectMarker + Send + Sync + core::fmt::Display + core::fmt::Debug,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report_collection().context(context)),
        }
    }

    #[inline(always)]
    fn context_with<C, F>(self, context: F) -> Result<V, Report<C, Mutable, SendSync>>
    where
        E: IntoReportCollection<SendSync>,
        F: FnOnce() -> C,
        C: ObjectMarker + Send + Sync + core::fmt::Display + core::fmt::Debug,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report_collection().context(context())),
        }
    }

    #[inline(always)]
    fn context_custom<H, C>(self, context: C) -> Result<V, Report<C, Mutable, SendSync>>
    where
        E: IntoReportCollection<SendSync>,
        C: ObjectMarker + Send + Sync,
        H: handlers::ContextHandler<C>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report_collection().context_custom::<H, _>(context)),
        }
    }

    #[inline(always)]
    fn context_custom_with<H, C, F>(self, context: F) -> Result<V, Report<C, Mutable, SendSync>>
    where
        E: IntoReportCollection<SendSync>,
        F: FnOnce() -> C,
        C: ObjectMarker + Send + Sync,
        H: handlers::ContextHandler<C>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report_collection().context_custom::<H, _>(context())),
        }
    }

    #[inline(always)]
    fn attach<A>(self, attachment: A) -> Result<V, Report<E::Context, Mutable, SendSync>>
    where
        E: IntoReport<SendSync, Ownership = Mutable>,
        A: ObjectMarker + Send + Sync + core::fmt::Display + core::fmt::Debug,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report().attach(attachment)),
        }
    }

    #[inline(always)]
    fn attach_with<A, F>(self, attachment: F) -> Result<V, Report<E::Context, Mutable, SendSync>>
    where
        E: IntoReport<SendSync, Ownership = Mutable>,
        F: FnOnce() -> A,
        A: ObjectMarker + Send + Sync + core::fmt::Display + core::fmt::Debug,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report().attach(attachment())),
        }
    }

    #[inline(always)]
    fn attach_custom<H, A>(self, attachment: A) -> Result<V, Report<E::Context, Mutable, SendSync>>
    where
        E: IntoReport<SendSync, Ownership = Mutable>,
        A: ObjectMarker + Send + Sync,
        H: handlers::AttachmentHandler<A>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report().attach_custom::<H, _>(attachment)),
        }
    }

    #[inline(always)]
    fn attach_custom_with<H, A, F>(
        self,
        attachment: F,
    ) -> Result<V, Report<E::Context, Mutable, SendSync>>
    where
        E: IntoReport<SendSync, Ownership = Mutable>,
        F: FnOnce() -> A,
        A: ObjectMarker + Send + Sync,
        H: handlers::AttachmentHandler<A>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report().attach_custom::<H, _>(attachment())),
        }
    }

    #[inline(always)]
    fn local_into_report(self) -> Result<V, Report<E::Context, E::Ownership, Local>>
    where
        E: IntoReport<Local>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report()),
        }
    }

    #[inline(always)]
    fn local_context<C>(self, context: C) -> Result<V, Report<C, Mutable, Local>>
    where
        E: IntoReportCollection<Local>,
        C: ObjectMarker + core::fmt::Display + core::fmt::Debug,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report_collection().context(context)),
        }
    }

    #[inline(always)]
    fn local_context_with<C, F>(self, context: F) -> Result<V, Report<C, Mutable, Local>>
    where
        E: IntoReportCollection<Local>,
        F: FnOnce() -> C,
        C: ObjectMarker + core::fmt::Display + core::fmt::Debug,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report_collection().context(context())),
        }
    }

    #[inline(always)]
    fn local_context_custom<H, C>(self, context: C) -> Result<V, Report<C, Mutable, Local>>
    where
        E: IntoReportCollection<Local>,
        C: ObjectMarker,
        H: handlers::ContextHandler<C>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report_collection().context_custom::<H, _>(context)),
        }
    }

    #[inline(always)]
    fn local_context_custom_with<H, C, F>(self, context: F) -> Result<V, Report<C, Mutable, Local>>
    where
        E: IntoReportCollection<Local>,
        F: FnOnce() -> C,
        C: ObjectMarker,
        H: handlers::ContextHandler<C>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report_collection().context_custom::<H, _>(context())),
        }
    }

    #[inline(always)]
    fn local_attach<A>(self, attachment: A) -> Result<V, Report<<E>::Context, Mutable, Local>>
    where
        E: IntoReport<Local, Ownership = Mutable>,
        A: ObjectMarker + core::fmt::Display + core::fmt::Debug,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report().attach(attachment)),
        }
    }

    #[inline(always)]
    fn local_attach_with<A, F>(
        self,
        attachment: F,
    ) -> Result<V, Report<<E>::Context, Mutable, Local>>
    where
        E: IntoReport<Local, Ownership = Mutable>,
        F: FnOnce() -> A,
        A: ObjectMarker + core::fmt::Display + core::fmt::Debug,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report().attach(attachment())),
        }
    }

    #[inline(always)]
    fn local_attach_custom<H, A>(
        self,
        attachment: A,
    ) -> Result<V, Report<<E>::Context, Mutable, Local>>
    where
        E: IntoReport<Local, Ownership = Mutable>,
        A: ObjectMarker,
        H: handlers::AttachmentHandler<A>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report().attach_custom::<H, _>(attachment)),
        }
    }

    #[inline(always)]
    fn local_attach_custom_with<H, A, F>(
        self,
        attachment: F,
    ) -> Result<V, Report<<E>::Context, Mutable, Local>>
    where
        E: IntoReport<Local, Ownership = Mutable>,
        F: FnOnce() -> A,
        A: ObjectMarker,
        H: handlers::AttachmentHandler<A>,
    {
        match self {
            Ok(v) => Ok(v),
            Err(e) => Err(e.into_report().attach_custom::<H, _>(attachment())),
        }
    }
}
