1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
// This file is part of tokio-interceptor.
//
// tokio-interceptor is free software: you can redistribute it and/or modify
// it under the terms of the GNU Lesser General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// tokio-interceptor is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public License
// along with tokio-interceptor.  If not, see <http://www.gnu.org/licenses/>.

use std::cell::{Ref,RefCell};
use std::rc::Rc;

use {Coeffect,NewCoeffect};
use effects::MutateState;

pub struct Db<State>(Rc<RefCell<State>>);

impl<State> Db<State> {
    pub fn new(state: State) -> Db<State> {
        Db(Rc::new(RefCell::new(state)))
    }

    pub fn borrow(&self) -> Ref<State> {
        self.0.borrow()
    }

    pub fn mutate<F>(&self, f: F) -> MutateState<State, F> {
        MutateState::new(Rc::clone(&self.0), f)
    }
}

impl<S> Clone for Db<S> {
    fn clone(&self) -> Db<S> {
        Db(Rc::clone(&self.0))
    }
}

impl<S: 'static> Coeffect for Db<S> {}

impl<S: 'static> NewCoeffect for Db<S> {
    type Instance = Db<S>;

    fn new_coeffect(&self) -> Db<S> {
        self.clone()
    }
}

#[cfg(test)]
mod tests {
    use super::*;

    use {Context,Event,Interceptor,InjectCoeffect,HandleEffects};
    use events::EventInterceptor;
    use tests::State;
    use futures::{future,Future};

    #[derive(Debug,Default,PartialEq)]
    struct Plus{
        initial: u8,
        inc: u8,
    }

    impl Plus {
        pub fn new(initial: u8, inc: u8) -> Plus {
            Plus { initial, inc }
        }
    }

    impl<E> Event<E> for Plus
    where E: 'static,
    {
        fn handle(self: Box<Self>, mut context: Context<E>) -> Box<Future<Item = Context<E>, Error = E>> {
            {
                let db = context.coeffects.get::<Db<State>>().unwrap();
                assert_eq!(self.initial, db.borrow().0);
                let inc = self.inc;
                context.effects.push(Box::new(db.mutate(move |state: &mut State| {
                    state.0 += inc;
                })));
            }
            Box::new(future::ok(context))
        }
    }

    #[test]
    fn test_event_as_interceptor() {
        let mut context: Context<()> = Context::new(vec![]);
        let event = Plus::new(101, 10);
        let db = Db::new(State(101));
        let i_state = InjectCoeffect::<Db<State>, ()>::new(db.clone());
        let i_effects: HandleEffects<()> = HandleEffects::new();
        let i_event = event;

        let queue = vec![Box::new(i_state) as Box<Interceptor<Error = ()>>,
                         Box::new(i_effects) as Box<Interceptor<Error = ()>>,
                         Box::new(EventInterceptor::new(i_event)) as Box<Interceptor<Error = ()>>];
        let mut stack = vec![];
        for i in queue.into_iter() {
            context = i.before(context).wait().unwrap();
            stack.push(i);
        }
        for i in stack.into_iter() {
            context = i.after(context).wait().unwrap();
        }

        assert_eq!(State(111), *db.borrow());
    }
}