use crate::TestCase;

/// Result of a testcase.
///
/// This includes both the returned value of the
/// testcase and the status.
///
/// # Example
///
/// ```
/// # use ensc_testsuite::{ PlanRunner, TestStatus };
/// # ensc_testsuite::doctest_prepare();
/// let result = PlanRunner::new().ok("test", || Result::<_,()>::Ok(42));
///
/// assert!(result.is_ok());
/// assert_eq!(result.as_status(), TestStatus::Pass);
/// assert_eq!(result.into_value(), Some(Ok(42)));
/// ```
pub struct Result<T> {
    ret:  Option<T>,
    case: TestCase,
}

impl <T> Result<T> {
    pub fn new(ret: Option<T>, case: TestCase) -> Self {
	Self {
	    ret: ret,
	    case: case,
	}
    }

    pub fn record_expect<V>(mut self, v: V) -> Self
    where
	V: std::fmt::Debug
    {
	self.case.report.record_expect(&format!("{:?}", v));
	self
    }

    pub fn record_got(mut self) -> Self
    where
	T: std::fmt::Debug
    {
	if let Some(v) = &self.ret {
	    self.case.report.record_got(&format!("{:?}", v));
	}
	self
    }

    pub fn record_got_fn<F,R>(mut self, f: F) -> Self
    where
	F: FnOnce(&T) -> R,
	R: std::fmt::Debug
    {
	if let Some(v) = &self.ret {
	    self.case.report.record_got(&format!("{:?}", f(v)));
	}
	self
    }

    pub fn map<F,R>(self, f: F) -> Result<R>
    where
	F: FnOnce(T) -> R
    {
	Result::new(self.ret.map(f), self.case)
    }

    pub fn is_ok(&self) -> bool {
	self.as_status().is_ok()
    }

    pub fn as_status(&self) -> crate::TestStatus {
	self.case.report.status
    }

    pub fn into_case(self) -> TestCase {
	self.case
    }

    pub fn into_value(self) -> Option<T> {
	self.ret
    }
}
