use std::fmt::Debug;
use std::error::Error;
use mio::Token;
use {Response, Time};
#[derive(Debug)]
pub enum ResponseImpl<M, N> {
Normal(M),
Deadline(M, Time),
Spawn(M, N),
Error(Box<Error>),
Done,
}
impl<M: Sized, N:Sized> Response<M, N> {
pub fn ok(machine: M) -> Response<M, N> {
Response(ResponseImpl::Normal(machine))
}
pub fn spawn(machine: M, result: N) -> Response<M, N> {
Response(ResponseImpl::Spawn(machine, result))
}
pub fn done() -> Response<M, N> {
Response::<M, N>(ResponseImpl::Done)
}
pub fn error(e: Box<Error>) -> Response<M, N> {
Response::<M, N>(ResponseImpl::Error(e))
}
pub fn deadline(self, time: Time) -> Response<M, N> {
let imp = match self.0 {
ResponseImpl::Normal(x) => ResponseImpl::Deadline(x, time),
ResponseImpl::Deadline(x, _) => ResponseImpl::Deadline(x, time),
ResponseImpl::Spawn(..) => {
panic!("You can't attach a deadline/timeout to the \
Response::spawn(). The `spawn` action is synchronous \
you must set a deadline in the `spawned` handler."); }
ResponseImpl::Done => {
panic!("You can't attach a deadline/timeout to \
Response::done() as it's useless. \
Timeout will never happen");
}
ResponseImpl::Error(_) => {
panic!("You can't attach a deadline/timeout to \
Response::error(_) as it's useless. \
Timeout will never happen");
}
};
Response(imp)
}
pub fn map<T, U, S, R>(self, self_mapper: S, result_mapper: R)
-> Response<T, U>
where S: FnOnce(M) -> T,
R: FnOnce(N) -> U,
{
use self::ResponseImpl::*;
let imp = match self.0 {
Normal(m) => Normal(self_mapper(m)),
Deadline(m, time) => Deadline(self_mapper(m), time),
Spawn(m, n) => Spawn(self_mapper(m), result_mapper(n)),
Done => Done,
Error(e) => Error(e),
};
Response(imp)
}
pub fn wrap<T, S>(self, self_mapper: S) -> Response<T, N>
where S: FnOnce(M) -> T
{
use self::ResponseImpl::*;
let imp = match self.0 {
Normal(m) => Normal(self_mapper(m)),
Deadline(m, time) => Deadline(self_mapper(m), time),
Spawn(m, n) => Spawn(self_mapper(m), n),
Done => Done,
Error(e) => Error(e),
};
Response(imp)
}
pub fn is_stopped(&self) -> bool {
use self::ResponseImpl::*;
match self.0 {
Normal(..) => false,
Deadline(..) => false,
Spawn(..) => false,
Done => true,
Error(..) => true,
}
}
pub fn cause(&self) -> Option<&Error> {
use self::ResponseImpl::*;
match self.0 {
Normal(..) => None,
Deadline(..) => None,
Spawn(..) => None,
Done => None,
Error(ref e) => Some(&**e),
}
}
}
impl<M: Sized + Debug, N: Sized + Debug> Response<M, N> {
pub fn expect_machine(self) -> M {
match self.0 {
ResponseImpl::Normal(x) => x,
ResponseImpl::Deadline(x, _) => x,
me => panic!("expected machine (`Response::ok(x)`), \
got {:?} instead", me),
}
}
pub fn expect_spawn(self) -> (M, N) {
match self.0 {
ResponseImpl::Spawn(x, y) => (x, y),
me => panic!("expected spawn (`Response::spawn(x)`), \
got {:?} instead", me),
}
}
pub fn expect_done(self) {
match self.0 {
ResponseImpl::Done => {}
me => panic!("expected done (`Response::done()`), \
got {:?} instead", me),
}
}
pub fn expect_error(self) -> Box<Error> {
match self.0 {
ResponseImpl::Error(e) => e,
me => panic!("expected error (`Response::error(e)`), \
got {:?} instead", me),
}
}
}
pub fn decompose<M, N>(token: Token, res: Response<M, N>)
-> (Result<M, Option<Box<Error>>>, Option<N>, Option<Time>)
{
match res.0 {
ResponseImpl::Normal(m) => (Ok(m), None, None),
ResponseImpl::Deadline(m, time) => (Ok(m), None, Some(time)),
ResponseImpl::Spawn(m, n) => (Ok(m), Some(n), None),
ResponseImpl::Done => (Err(None), None, None),
ResponseImpl::Error(e) => {
if cfg!(log_errors) {
warn!("State machine {:?} exited with error: {}", token, e);
}
(Err(Some(e)), None, None)
}
}
}
#[cfg(test)]
mod tests {
use super::super::Response;
#[test]
fn size_of_response() {
assert_eq!(::std::mem::size_of::<Response<u64, u64>>(), 24)
}
}