use super::{super::super::problem::*, into::*, problem::*};

use std::error::Error;

//
// IntoSerdeProblemResult
//

/// Maps [Err] into a [SerdeProblem].
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub trait IntoSerdeProblemResult<OkT> {
    /// Maps [Err] into a [SerdeProblem] via
    /// [SerializationError](super::super::super::common::SerializationError).
    ///
    /// You probably want to call [from_serde_problem](FromSerdeProblemResult::from_serde_problem)
    /// to eventually map it back and avoid re-wrapping it and thus losing the causation chain.
    fn into_serde_serialize_problem(self) -> Result<OkT, SerdeProblem>;

    /// Maps [Err] into a [SerdeProblem] via
    /// [DeserializationError](super::super::super::common::DeserializationError).
    ///
    /// You probably want to call [from_serde_problem](FromSerdeProblemResult::from_serde_problem)
    /// to eventually map it back and avoid re-wrapping it and thus losing the causation chain.
    fn into_serde_deserialize_problem(self) -> Result<OkT, SerdeProblem>;
}

// Note that we are *not* using map_err() here because a closure cannot be annotated with #[track_caller]

impl<OkT, ErrorT> IntoSerdeProblemResult<OkT> for Result<OkT, ErrorT>
where
    ErrorT: 'static + Error + Send + Sync,
{
    #[track_caller]
    fn into_serde_serialize_problem(self) -> Result<OkT, SerdeProblem> {
        match self {
            Ok(ok) => Ok(ok),
            Err(error) => Err(error.into_serde_serialize_problem()),
        }
    }

    #[track_caller]
    fn into_serde_deserialize_problem(self) -> Result<OkT, SerdeProblem> {
        match self {
            Ok(ok) => Ok(ok),
            Err(error) => Err(error.into_serde_deserialize_problem()),
        }
    }
}

//
// FromSerdeProblemResult
//

/// Maps a [SerdeProblem] [Err] into a [Problem].
#[cfg_attr(docsrs, doc(cfg(feature = "serde")))]
pub trait FromSerdeProblemResult<OkT> {
    /// Maps a [SerdeProblem] [Err] into a [Problem].
    fn from_serde_problem(self) -> Result<OkT, Problem>;
}

impl<OkT> FromSerdeProblemResult<OkT> for Result<OkT, SerdeProblem> {
    fn from_serde_problem(self) -> Result<OkT, Problem> {
        self.map_err(|serde_problem| serde_problem.problem)
    }
}
