use super::super::problem::*;

impl From<Problem> for wasmtime::Error {
    fn from(problem: Problem) -> Self {
        if !problem.causes.is_empty() {
            let mut causes = problem.into_iter().rev();
            let root_cause = causes.next().expect("root cause");
            let mut wasmtime_error = wasmtime::Error::from_boxed(root_cause.error);
            for cause in causes {
                wasmtime_error = wasmtime_error.context(cause.error);
            }
            wasmtime_error
        } else {
            problem.into_error().into()
        }
    }
}

//
// WasmtimeIntoProblem
//

/// Into problem.
pub trait WasmtimeIntoProblem {
    /// Into problem.
    fn into_problem(self) -> Problem;
}

impl WasmtimeIntoProblem for wasmtime::Error {
    #[track_caller]
    fn into_problem(self) -> Problem {
        Problem::new(self.into())
    }
}

//
// WasmtimeIntoProblemResult
//

/// Into [Result]\<_, [Problem]\>.
pub trait WasmtimeIntoProblemResult<OkT> {
    /// Into [Result]\<_, [Problem]\>.
    fn into_problem(self) -> Result<OkT, Problem>;
}

impl<OkT> WasmtimeIntoProblemResult<OkT> for wasmtime::Result<OkT> {
    #[track_caller]
    fn into_problem(self) -> Result<OkT, Problem> {
        // Note that we are *not* using map_err() here because a closure cannot be annotated with #[track_caller]
        match self {
            Ok(ok) => Ok(ok),
            Err(error) => Err(error.into_problem()),
        }
    }
}
