<commit_before>#![deny(missing_docs, warnings)]

//! Request logging middleware for Iron

extern crate iron;
extern crate time;
extern crate term;

use iron::{AfterMiddleware, BeforeMiddleware, IronResult, IronError, Request, Response, status};
use iron::typemap::Key;
use term::{Terminal, stdout};

use std::io;
use std::error::Error;
use std::fmt::{self, Display, Formatter};

use format::FormatText::{Str, Method, URI, Status, ResponseTime};
use format::FormatColor::{ConstantColor, FunctionColor};
use format::FormatAttr::{ConstantAttrs, FunctionAttrs};
use format::{Format, FormatText};

pub mod format;

/// `Middleware` for logging request and response info to the terminal.
pub struct Logger {
    format: Option<Format>
}

impl Logger {
    /// Create a pair of `Logger` middlewares with the specified `format`. If a `None` is passed in, uses the default format:
    ///
    /// ```ignore
    /// {method} {uri} -> {status} ({response_time} ms)
    /// ```
    ///
    /// While the returned value can be passed straight to `Chain::link`, consider making the logger `BeforeMiddleware`
    /// the first in your chain and the logger `AfterMiddleware` the last by doing something like this:
    ///
    /// ```ignore
    /// let mut chain = Chain::new(handler);
    /// let (logger_before, logger_after) = Logger::new(None);
    /// chain.link_before(logger_before);
    /// // link other middlewares here...
    /// chain.link_after(logger_after);
    /// ```
    pub fn new(format: Option<Format>) -> (Logger, Logger) {
        (Logger { format: format.clone() }, Logger { format: format })
    }
}

struct StartTime;
impl Key for StartTime { type Value = u64; }

impl BeforeMiddleware for Logger {
    fn before(&self, req: &mut Request) -> IronResult<()> {
        req.extensions.insert::<StartTime>(time::precise_time_ns());
        Ok(())
    }

    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<()> {
        Err(err)
    }
}

impl AfterMiddleware for Logger {
    fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
        let exit_time = time::precise_time_ns();
        let entry_time = *req.extensions.get::<StartTime>().unwrap();

        let response_time_ms = (exit_time - entry_time) as f64 / 1000000.0;
        let Format(format) = self.format.clone().unwrap_or_default();

        {
            let render = |text: &FormatText| {
                match *text {
                    Str(ref string) => string.clone(),
                    Method => format!("{}", req.method),
                    URI => format!("{}", req.url),
                    Status => format!("{}", res.status.unwrap()),
                    ResponseTime => format!("{} ms", response_time_ms)
                }
            };

            let log = |mut t: Box<Terminal<io::Stdout> + Send>| -> io::Result<()> {
                for unit in format.iter() {
                    match unit.color {
                        ConstantColor(Some(color)) => { try!(t.fg(color)); }
                        ConstantColor(None) => (),
                        FunctionColor(f) => match f(req, &res) {
                            Some(color) => { try!(t.fg(color)); }
                            None => ()
                        }
                    }
                    match unit.attrs {
                        ConstantAttrs(ref attrs) => {
                            for &attr in attrs.iter() {
                                try!(t.attr(attr));
                            }
                        }
                        FunctionAttrs(f) => {
                            for &attr in f(req, &res).iter() {
                                try!(t.attr(attr));
                            }
                        }
                    }
                    try!(write!(t, "{}", render(&unit.text)));
                    try!(t.reset());
                }
                try!(writeln!(t, ""));
                Ok(())
            };

            match stdout() {
                Some(terminal) => {
                    match log(terminal) {
                        Ok(result) => result,
                        Err(err) => return Err(IronError::new(err, status::InternalServerError))
                    }
                }
                None => { return Err(IronError::new(CouldNotOpenTerminal,
                                                    status::InternalServerError)) }
            };
        }

        Ok(res)
    }

    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        Err(err)
    }
}

/// Error returned when logger cannout access stdout.
#[derive(Debug, Clone, Copy)]
pub struct CouldNotOpenTerminal;

impl Error for CouldNotOpenTerminal {
    fn description(&self) -> &str {
        "Could Not Open Terminal"
    }
}

impl Display for CouldNotOpenTerminal {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Logger could not open stdout as a terminal.")
    }
}
<commit_msg>(feat) Log errors<commit_after>#![deny(missing_docs, warnings)]

//! Request logging middleware for Iron

extern crate iron;
extern crate time;
extern crate term;

use iron::{AfterMiddleware, BeforeMiddleware, IronResult, IronError, Request, Response, status};
use iron::typemap::Key;
use term::{Terminal, stdout};

use std::io;
use std::error::Error;
use std::fmt::{self, Display, Formatter};

use format::FormatText::{Str, Method, URI, Status, ResponseTime};
use format::FormatColor::{ConstantColor, FunctionColor};
use format::FormatAttr::{ConstantAttrs, FunctionAttrs};
use format::{Format, FormatText};

pub mod format;

/// `Middleware` for logging request and response info to the terminal.
pub struct Logger {
    format: Option<Format>
}

impl Logger {
    /// Create a pair of `Logger` middlewares with the specified `format`. If a `None` is passed in, uses the default format:
    ///
    /// ```ignore
    /// {method} {uri} -> {status} ({response_time} ms)
    /// ```
    ///
    /// While the returned value can be passed straight to `Chain::link`, consider making the logger `BeforeMiddleware`
    /// the first in your chain and the logger `AfterMiddleware` the last by doing something like this:
    ///
    /// ```ignore
    /// let mut chain = Chain::new(handler);
    /// let (logger_before, logger_after) = Logger::new(None);
    /// chain.link_before(logger_before);
    /// // link other middlewares here...
    /// chain.link_after(logger_after);
    /// ```
    pub fn new(format: Option<Format>) -> (Logger, Logger) {
        (Logger { format: format.clone() }, Logger { format: format })
    }
}

struct StartTime;
impl Key for StartTime { type Value = u64; }

impl Logger {
    fn initialise(&self, req: &mut Request) {
        req.extensions.insert::<StartTime>(time::precise_time_ns());
    }

    fn log(&self, req: &mut Request, res: &Response) -> IronResult<()> {
        let exit_time = time::precise_time_ns();
        let entry_time = *req.extensions.get::<StartTime>().unwrap();

        let response_time_ms = (exit_time - entry_time) as f64 / 1000000.0;
        let Format(format) = self.format.clone().unwrap_or_default();

        {
            let render = |text: &FormatText| {
                match *text {
                    Str(ref string) => string.clone(),
                    Method => format!("{}", req.method),
                    URI => format!("{}", req.url),
                    Status => format!("{}", res.status.unwrap()),
                    ResponseTime => format!("{} ms", response_time_ms)
                }
            };

            let log = |mut t: Box<Terminal<io::Stdout> + Send>| -> io::Result<()> {
                for unit in format.iter() {
                    match unit.color {
                        ConstantColor(Some(color)) => { try!(t.fg(color)); }
                        ConstantColor(None) => (),
                        FunctionColor(f) => match f(req, res) {
                            Some(color) => { try!(t.fg(color)); }
                            None => ()
                        }
                    }
                    match unit.attrs {
                        ConstantAttrs(ref attrs) => {
                            for &attr in attrs.iter() {
                                try!(t.attr(attr));
                            }
                        }
                        FunctionAttrs(f) => {
                            for &attr in f(req, res).iter() {
                                try!(t.attr(attr));
                            }
                        }
                    }
                    try!(write!(t, "{}", render(&unit.text)));
                    try!(t.reset());
                }
                try!(writeln!(t, ""));
                Ok(())
            };

            match stdout() {
                Some(terminal) => {
                    match log(terminal) {
                        Ok(result) => result,
                        Err(err) => return Err(IronError::new(err, status::InternalServerError))
                    }
                }
                None => { return Err(IronError::new(CouldNotOpenTerminal,
                                                    status::InternalServerError)) }
            };
        }

        Ok(())
    }
}

impl BeforeMiddleware for Logger {
    fn before(&self, req: &mut Request) -> IronResult<()> {
        self.initialise(req);
        Ok(())
    }

    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<()> {
        self.initialise(req);
        Err(err)
    }
}

impl AfterMiddleware for Logger {
    fn after(&self, req: &mut Request, res: Response) -> IronResult<Response> {
        match self.log(req, &res) {
            Ok(_) => Ok(res),
            Err(log_err) => Err(log_err),
        }
    }

    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<Response> {
        match self.log(req, &err.response) {
            Ok(_) => Err(err),
            Err(log_err) => Err(log_err),
        }
    }
}

/// Error returned when logger cannout access stdout.
#[derive(Debug, Clone, Copy)]
pub struct CouldNotOpenTerminal;

impl Error for CouldNotOpenTerminal {
    fn description(&self) -> &str {
        "Could Not Open Terminal"
    }
}

impl Display for CouldNotOpenTerminal {
    fn fmt(&self, f: &mut Formatter) -> fmt::Result {
        write!(f, "Logger could not open stdout as a terminal.")
    }
}
<|endoftext|>


<commit_before>trait DOMNode {}
impl<'a, T: DOMNode> DOMNode for &'a T {}

trait DOMLevel {
    type NodeType: DOMNode;
    type ChildrenType: DOMChildren;
}
impl<T: DOMNode> DOMLevel for T {
    type NodeType = T;
    type ChildrenType = ();
}

/// Processor that can fold over all the members of a `DOMLevel`
trait DOMChildrenProcessor {
    /// Accumulator
    type Acc;

    /// Folding function
    fn get_processor<T: DOMChildren>() -> fn(&mut Self::Acc, &T) -> ();
}

trait DOMChildren {
    fn process_all<P: DOMChildrenProcessor>(&self, acc: &mut P::Acc) -> ();
}

impl DOMChildren for () {
    fn process_all<P: DOMChildrenProcessor>(&self, _acc: &mut P::Acc) -> () {}
}

impl<T: DOMLevel> DOMChildren for T {
    fn process_all<P: DOMChildrenProcessor>(&self, acc: &mut P::Acc) -> () {
        P::get_processor()(acc, self);
    }
}

impl<T: DOMChildren> DOMChildren for [T] {
    fn process_all<P: DOMChildrenProcessor>(&self, acc: &mut P::Acc) -> () {
        for x in self {
            x.process_all::<P>(acc);
        }
    }
}

macro_rules! array_impls {
    ($($len:expr,)*) => { $(
        impl<T: DOMChildren> DOMChildren for [T; $len] {
            fn process_all<P: DOMChildrenProcessor>(&self, acc: &mut P::Acc) -> () {
                for x in self {
                    x.process_all::<P>(acc);
                }
            }
        }
    )* }
}

array_impls!(
    0, 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,
);

// Credit to @shepmaster for structure of recursive tuple macro
macro_rules! tuple_impls {
    () => {};

    // Copywrite @shepmaster
    (($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
        /*
         * Invoke recursive reversal of list that ends in the macro expansion implementation
         * of the reversed list
        */
        tuple_impls!([($idx, $typ);] $( ($nidx => $ntyp), )*);
        tuple_impls!($( ($nidx => $ntyp), )*); // invoke macro on tail
    };

    /*
     * ([accumulatedList], listToReverse); recursively calls tuple_impls until the list to reverse
     + is empty (see next pattern)
    */
    ([$(($accIdx: tt, $accTyp: ident);)+]  ($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
      tuple_impls!([($idx, $typ); $(($accIdx, $accTyp); )*] $( ($nidx => $ntyp), ) *);
    };

    // Finally expand into the implementation
    ([($idx:tt, $typ:ident); $( ($nidx:tt, $ntyp:ident); )*]) => {
        impl<$typ, $( $ntyp ),*> DOMChildren for ($typ, $( $ntyp ),*)
            where $typ: DOMChildren + Copy,
                  $( $ntyp: DOMChildren + Copy ),*
        {
            fn process_all<P>(&self, acc: &mut P::Acc) -> ()
                    where P: DOMChildrenProcessor {
                &self.$idx.process_all::<P>(acc);
                $(
                    &self.$nidx.process_all::<P>(acc);
                )*
            }
        }
    }
}

tuple_impls!(
    (9 => J),
    (8 => I),
    (7 => H),
    (6 => G),
    (5 => F),
    (4 => E),
    (3 => D),
    (2 => C),
    (1 => B),
    (0 => A),
);

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

    #[derive(Copy, Clone)]
    struct BogusOne;
    impl DOMNode for BogusOne {}

    struct BogusTwo;
    impl DOMNode for BogusTwo {}

    struct ChildCounter;
    impl DOMChildrenProcessor for ChildCounter {
        type Acc = usize;

        fn get_processor<T: DOMChildren>() -> fn(&mut Self::Acc, &T) -> () {
            fn incr<T: DOMChildren>(state: &mut usize, _level: &T) {
                *state += 1;
            }
            incr
        }
    }

    #[test]
    fn counts_children() {
        let mut count = 0;
        (BogusOne, &BogusOne, &BogusTwo).process_all::<ChildCounter>(&mut count);
        assert_eq!(3, count);

        count = 0;
        (BogusOne, (BogusOne,), BogusOne).process_all::<ChildCounter>(&mut count);
        assert_eq!(3, count);

        count = 0;
        [BogusOne, BogusOne, BogusOne].process_all::<ChildCounter>(&mut count);
        assert_eq!(3, count);

        count = 0;
        (BogusOne, BogusOne,
            [BogusOne, BogusOne, BogusOne],
            [(BogusOne)],
            [(), (), ()],
            [&BogusTwo, &BogusTwo, &BogusTwo],
        ).process_all::<ChildCounter>(&mut count);
        assert_eq!(9, count);
    }
}
<commit_msg>Consolidate DOMLevel and DOMNode<commit_after>trait DOMNode {
    type ChildrenType: DOMChildren;
}
impl<'a, T: DOMNode> DOMNode for &'a T {
    type ChildrenType = T::ChildrenType;
}

struct Div<C: DOMChildren>(C);
impl<C: DOMChildren> DOMNode for Div<C> {
    type ChildrenType = C;
}

/// Processor that can fold over all the children of a `DOMNode`
trait DOMChildrenProcessor {
    /// Accumulator
    type Acc;

    /// Folding function
    fn get_processor<T: DOMChildren>() -> fn(&mut Self::Acc, &T) -> ();
}

trait DOMChildren {
    fn process_all<P: DOMChildrenProcessor>(&self, acc: &mut P::Acc) -> ();
}

impl DOMChildren for () {
    fn process_all<P: DOMChildrenProcessor>(&self, _acc: &mut P::Acc) -> () {}
}

impl<T: DOMNode> DOMChildren for T {
    fn process_all<P: DOMChildrenProcessor>(&self, acc: &mut P::Acc) -> () {
        P::get_processor()(acc, self);
    }
}

impl<T: DOMChildren> DOMChildren for [T] {
    fn process_all<P: DOMChildrenProcessor>(&self, acc: &mut P::Acc) -> () {
        for x in self {
            x.process_all::<P>(acc);
        }
    }
}

macro_rules! array_impls {
    ($($len:expr,)*) => { $(
        impl<T: DOMChildren> DOMChildren for [T; $len] {
            fn process_all<P: DOMChildrenProcessor>(&self, acc: &mut P::Acc) -> () {
                for x in self {
                    x.process_all::<P>(acc);
                }
            }
        }
    )* }
}

array_impls!(
    0, 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,
);

// Credit to @shepmaster for structure of recursive tuple macro
macro_rules! tuple_impls {
    () => {};

    // Copywrite @shepmaster
    (($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
        /*
         * Invoke recursive reversal of list that ends in the macro expansion implementation
         * of the reversed list
        */
        tuple_impls!([($idx, $typ);] $( ($nidx => $ntyp), )*);
        tuple_impls!($( ($nidx => $ntyp), )*); // invoke macro on tail
    };

    /*
     * ([accumulatedList], listToReverse); recursively calls tuple_impls until the list to reverse
     + is empty (see next pattern)
    */
    ([$(($accIdx: tt, $accTyp: ident);)+]  ($idx:tt => $typ:ident), $( ($nidx:tt => $ntyp:ident), )*) => {
      tuple_impls!([($idx, $typ); $(($accIdx, $accTyp); )*] $( ($nidx => $ntyp), ) *);
    };

    // Finally expand into the implementation
    ([($idx:tt, $typ:ident); $( ($nidx:tt, $ntyp:ident); )*]) => {
        impl<$typ, $( $ntyp ),*> DOMChildren for ($typ, $( $ntyp ),*)
            where $typ: DOMChildren + Copy,
                  $( $ntyp: DOMChildren + Copy ),*
        {
            fn process_all<P>(&self, acc: &mut P::Acc) -> ()
                    where P: DOMChildrenProcessor {
                &self.$idx.process_all::<P>(acc);
                $(
                    &self.$nidx.process_all::<P>(acc);
                )*
            }
        }
    }
}

tuple_impls!(
    (9 => J),
    (8 => I),
    (7 => H),
    (6 => G),
    (5 => F),
    (4 => E),
    (3 => D),
    (2 => C),
    (1 => B),
    (0 => A),
);

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

    #[derive(Copy, Clone)]
    struct BogusOne;
    impl DOMNode for BogusOne {
        type ChildrenType = ();
    }

    struct BogusTwo;
    impl DOMNode for BogusTwo {
        type ChildrenType = ();
    }

    struct ChildCounter;
    impl DOMChildrenProcessor for ChildCounter {
        type Acc = usize;

        fn get_processor<T: DOMChildren>() -> fn(&mut Self::Acc, &T) -> () {
            fn incr<T: DOMChildren>(state: &mut usize, _level: &T) {
                *state += 1;
            }
            incr
        }
    }

    #[test]
    fn counts_children() {
        let mut count = 0;
        (BogusOne, &BogusOne, &BogusTwo).process_all::<ChildCounter>(&mut count);
        assert_eq!(3, count);

        count = 0;
        (BogusOne, (BogusOne,), BogusOne).process_all::<ChildCounter>(&mut count);
        assert_eq!(3, count);

        count = 0;
        [BogusOne, BogusOne, BogusOne].process_all::<ChildCounter>(&mut count);
        assert_eq!(3, count);

        count = 0;
        (BogusOne, BogusOne,
            [BogusOne, BogusOne, BogusOne],
            [(BogusOne)],
            [(), (), ()],
            [&BogusTwo, &BogusTwo, &BogusTwo],
        ).process_all::<ChildCounter>(&mut count);
        assert_eq!(9, count);
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(all(feature="nightly", test), feature(test))]
#![cfg_attr(feature="nightly", feature(const_fn))]

#[macro_use]
#[cfg(test)]
extern crate lazy_static;

use std::thread::{self, JoinHandle};
use std::sync::mpsc::{channel, Sender, Receiver, SyncSender, sync_channel};
use std::sync::{Arc, Mutex};
use std::marker::PhantomData;

enum Message {
    NewJob(Thunk<'static>),
    Join,
}

trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce()> FnBox for F {
    fn call_box(self: Box<F>) {
        (*self)()
    }
}

type Thunk<'a> = Box<FnBox + Send + 'a>;

impl Drop for PoolCache {
    fn drop(&mut self) {
        ::std::mem::replace(&mut self.job_sender, None);
    }
}

pub struct PoolCache {
    threads: Vec<ThreadData>,
    job_sender: Option<Sender<Message>>
}

struct ThreadData {
    _thread_join_handle: JoinHandle<()>,
    pool_sync_rx: Receiver<()>,
    thread_sync_tx: SyncSender<()>,
}

impl PoolCache {
    pub fn new(n: u32) -> PoolCache {
        assert!(n >= 1);

        let (job_sender, job_receiver) = channel();
        let job_receiver = Arc::new(Mutex::new(job_receiver));

        let mut threads = Vec::with_capacity(n as usize);

        // spawn n threads, put them in waiting mode
        for _ in 0..n {
            let job_receiver = job_receiver.clone();

            let (pool_sync_tx, pool_sync_rx) =
                sync_channel::<()>(0);
            let (thread_sync_tx, thread_sync_rx) =
                sync_channel::<()>(0);

            let thread = thread::spawn(move || {
                loop {
                    let message = {
                        // Only lock jobs for the time it takes
                        // to get a job, not run it.
                        let lock = job_receiver.lock().unwrap();
                        lock.recv()
                    };

                    match message {
                        Ok(Message::NewJob(job)) => {
                            job.call_box();
                        }
                        Ok(Message::Join) => {
                            // syncronize with pool
                            if pool_sync_tx.send(()).is_err() {
                                // The pool was dropped.
                                break;
                            }
                            if thread_sync_rx.recv().is_err() {
                                // The pool was dropped.
                                break;
                            }
                        }
                        Err(..) => {
                            // The pool was dropped.
                            break
                        }
                    }
                }
            });

            threads.push(ThreadData {
                _thread_join_handle: thread,
                pool_sync_rx: pool_sync_rx,
                thread_sync_tx: thread_sync_tx,
            });
        }

        PoolCache {
            threads: threads,
            job_sender: Some(job_sender),
        }
    }

    pub fn scope<'pool, 'scope, F, R>(&'pool mut self, f: F) -> R
        where F: FnOnce(&Scope<'pool, 'scope>) -> R
    {
        let scope = Scope {
            pool: self,
            _marker: PhantomData,
        };
        f(&scope)
    }

    pub fn thread_count(&self) -> u32 {
        self.threads.len() as u32
    }
}

/////////////////////////////////////////////////////////////////////////////

pub struct Scope<'pool, 'scope> {
    pool: &'pool mut PoolCache,
    _marker: PhantomData<&'scope mut ()>,
}

impl<'pool, 'scope> Scope<'pool, 'scope> {
    pub fn execute<F>(&self, f: F)
        where F: FnOnce() + Send + 'scope
    {
        let b = unsafe {
            ::std::mem::transmute::<Thunk<'scope>, Thunk<'static>>(Box::new(f))
        };
        self.pool.job_sender.as_ref().unwrap().send(Message::NewJob(b)).unwrap();
    }
}

impl<'pool, 'scope> Drop for Scope<'pool, 'scope> {
    fn drop(&mut self) {
        for _ in 0..self.pool.threads.len() {
            self.pool.job_sender.as_ref().unwrap().send(Message::Join).unwrap();
        }

        // syncronize with threads
        for thread_data in &self.pool.threads {
            thread_data.pool_sync_rx.recv().unwrap();
        }
        for thread_data in &self.pool.threads {
            thread_data.thread_sync_tx.send(()).unwrap();
        }
    }
}

/////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn example() {
        // Create a threadpool holding 4 threads
        let mut pool = PoolCache::new(4);

        let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7];

        // Use the threads as scoped threads that can
        // reference anything outside this closure
        pool.scope(|scoped| {

            // Create references to each element in the vector ...
            for e in &mut vec {
                // ... and add 1 to it in a seperate thread
                scoped.execute(move || {
                    *e += 1;
                });
            }
        });

        assert_eq!(vec, vec![1, 2, 3, 4, 5, 6, 7, 8]);
    }

    #[test]
    fn smoketest() {
        let mut pool = PoolCache::new(4);

        for i in 1..7 {
            let mut vec = vec![0, 1, 2, 3, 4];
            pool.scope(|s| {
                for e in vec.iter_mut() {
                    s.execute(move || {
                        *e += i;
                    });
                }
            });

            let mut vec2 = vec![0, 1, 2, 3, 4];
            for e in vec2.iter_mut() {
                *e += i;
            }

            assert_eq!(vec, vec2);
        }
    }

    #[test]
    #[should_panic]
    fn panicking() {
        let mut pool = PoolCache::new(4);
        pool.scope(|scoped| {
            scoped.execute(move || {
                panic!()
            });
        });
    }
}

#[cfg(all(test, feature="nightly"))]
mod benches {
    extern crate test;

    use self::test::{Bencher, black_box};
    use super::PoolCache;
    use std::sync::Mutex;

    // const MS_SLEEP_PER_OP: u32 = 1;

    lazy_static! {
        static ref POOL_1: Mutex<PoolCache> = Mutex::new(PoolCache::new(1));
        static ref POOL_2: Mutex<PoolCache> = Mutex::new(PoolCache::new(2));
        static ref POOL_3: Mutex<PoolCache> = Mutex::new(PoolCache::new(3));
        static ref POOL_4: Mutex<PoolCache> = Mutex::new(PoolCache::new(4));
        static ref POOL_5: Mutex<PoolCache> = Mutex::new(PoolCache::new(5));
        static ref POOL_8: Mutex<PoolCache> = Mutex::new(PoolCache::new(8));
    }

    fn fib(n: u64) -> u64 {
        let mut prev_prev: u64 = 1;
        let mut prev = 1;
        let mut current = 1;
        for _ in 2..(n+1) {
            current = prev_prev.wrapping_add(prev);
            prev_prev = prev;
            prev = current;
        }
        current
    }

    fn threads_interleaved_n(pool: &mut PoolCache)  {
        let size = 1024; // 1kiB

        let mut data = vec![1u8; size];
        pool.scope(|s| {
            for e in data.iter_mut() {
                s.execute(move || {
                    *e += fib(black_box(1000 * (*e as u64))) as u8;
                    //thread::sleep_ms(MS_SLEEP_PER_OP);
                });
            }
        });
    }

    #[bench]
    fn threads_interleaved_1(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_1.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_2(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_2.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_4(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_4.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_8(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_8.lock().unwrap()))
    }

    fn threads_chunked_n(pool: &mut PoolCache) {
        let size = 1024 * 1024 * 100 / 4; // 100MiB

        let n = pool.thread_count();
        let mut data = vec![0u32; size];
        pool.scope(|s| {
            let l = (data.len() - 1) / n as usize + 1;
            for es in data.chunks_mut(l) {
                s.execute(move || {
                    if es.len() > 1 {
                        es[0] = 1;
                        es[1] = 1;
                        for i in 2..es.len() {
                            // Fibonnaci gets big fast,
                            // so just wrap around all the time
                            es[i] = es[i-1].wrapping_add(es[i-2]);
                        }
                    }
                    //thread::sleep_ms(MS_SLEEP_PER_OP);
                });
            }
        });
    }

    #[bench]
    fn threads_chunked_1(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_1.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_2(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_2.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_3(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_3.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_4(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_4.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_5(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_5.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_8(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_8.lock().unwrap()))
    }
}
<commit_msg>Add another panic test<commit_after>#![cfg_attr(all(feature="nightly", test), feature(test))]
#![cfg_attr(feature="nightly", feature(const_fn))]

#[macro_use]
#[cfg(test)]
extern crate lazy_static;

use std::thread::{self, JoinHandle};
use std::sync::mpsc::{channel, Sender, Receiver, SyncSender, sync_channel};
use std::sync::{Arc, Mutex};
use std::marker::PhantomData;

enum Message {
    NewJob(Thunk<'static>),
    Join,
}

trait FnBox {
    fn call_box(self: Box<Self>);
}

impl<F: FnOnce()> FnBox for F {
    fn call_box(self: Box<F>) {
        (*self)()
    }
}

type Thunk<'a> = Box<FnBox + Send + 'a>;

impl Drop for PoolCache {
    fn drop(&mut self) {
        ::std::mem::replace(&mut self.job_sender, None);
    }
}

pub struct PoolCache {
    threads: Vec<ThreadData>,
    job_sender: Option<Sender<Message>>
}

struct ThreadData {
    _thread_join_handle: JoinHandle<()>,
    pool_sync_rx: Receiver<()>,
    thread_sync_tx: SyncSender<()>,
}

impl PoolCache {
    pub fn new(n: u32) -> PoolCache {
        assert!(n >= 1);

        let (job_sender, job_receiver) = channel();
        let job_receiver = Arc::new(Mutex::new(job_receiver));

        let mut threads = Vec::with_capacity(n as usize);

        // spawn n threads, put them in waiting mode
        for _ in 0..n {
            let job_receiver = job_receiver.clone();

            let (pool_sync_tx, pool_sync_rx) =
                sync_channel::<()>(0);
            let (thread_sync_tx, thread_sync_rx) =
                sync_channel::<()>(0);

            let thread = thread::spawn(move || {
                loop {
                    let message = {
                        // Only lock jobs for the time it takes
                        // to get a job, not run it.
                        let lock = job_receiver.lock().unwrap();
                        lock.recv()
                    };

                    match message {
                        Ok(Message::NewJob(job)) => {
                            job.call_box();
                        }
                        Ok(Message::Join) => {
                            // syncronize with pool
                            if pool_sync_tx.send(()).is_err() {
                                // The pool was dropped.
                                break;
                            }
                            if thread_sync_rx.recv().is_err() {
                                // The pool was dropped.
                                break;
                            }
                        }
                        Err(..) => {
                            // The pool was dropped.
                            break
                        }
                    }
                }
            });

            threads.push(ThreadData {
                _thread_join_handle: thread,
                pool_sync_rx: pool_sync_rx,
                thread_sync_tx: thread_sync_tx,
            });
        }

        PoolCache {
            threads: threads,
            job_sender: Some(job_sender),
        }
    }

    pub fn scope<'pool, 'scope, F, R>(&'pool mut self, f: F) -> R
        where F: FnOnce(&Scope<'pool, 'scope>) -> R
    {
        let scope = Scope {
            pool: self,
            _marker: PhantomData,
        };
        f(&scope)
    }

    pub fn thread_count(&self) -> u32 {
        self.threads.len() as u32
    }
}

/////////////////////////////////////////////////////////////////////////////

pub struct Scope<'pool, 'scope> {
    pool: &'pool mut PoolCache,
    _marker: PhantomData<&'scope mut ()>,
}

impl<'pool, 'scope> Scope<'pool, 'scope> {
    pub fn execute<F>(&self, f: F)
        where F: FnOnce() + Send + 'scope
    {
        let b = unsafe {
            ::std::mem::transmute::<Thunk<'scope>, Thunk<'static>>(Box::new(f))
        };
        self.pool.job_sender.as_ref().unwrap().send(Message::NewJob(b)).unwrap();
    }
}

impl<'pool, 'scope> Drop for Scope<'pool, 'scope> {
    fn drop(&mut self) {
        for _ in 0..self.pool.threads.len() {
            self.pool.job_sender.as_ref().unwrap().send(Message::Join).unwrap();
        }

        // syncronize with threads
        for thread_data in &self.pool.threads {
            thread_data.pool_sync_rx.recv().unwrap();
        }
        for thread_data in &self.pool.threads {
            thread_data.thread_sync_tx.send(()).unwrap();
        }
    }
}

/////////////////////////////////////////////////////////////////////////////

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

    #[test]
    fn example() {
        // Create a threadpool holding 4 threads
        let mut pool = PoolCache::new(4);

        let mut vec = vec![0, 1, 2, 3, 4, 5, 6, 7];

        // Use the threads as scoped threads that can
        // reference anything outside this closure
        pool.scope(|scoped| {

            // Create references to each element in the vector ...
            for e in &mut vec {
                // ... and add 1 to it in a seperate thread
                scoped.execute(move || {
                    *e += 1;
                });
            }
        });

        assert_eq!(vec, vec![1, 2, 3, 4, 5, 6, 7, 8]);
    }

    #[test]
    fn smoketest() {
        let mut pool = PoolCache::new(4);

        for i in 1..7 {
            let mut vec = vec![0, 1, 2, 3, 4];
            pool.scope(|s| {
                for e in vec.iter_mut() {
                    s.execute(move || {
                        *e += i;
                    });
                }
            });

            let mut vec2 = vec![0, 1, 2, 3, 4];
            for e in vec2.iter_mut() {
                *e += i;
            }

            assert_eq!(vec, vec2);
        }
    }

    #[test]
    #[should_panic]
    fn thread_panic() {
        let mut pool = PoolCache::new(4);
        pool.scope(|scoped| {
            scoped.execute(move || {
                panic!()
            });
        });
    }

    #[test]
    #[should_panic]
    fn pool_panic() {
        let _pool = PoolCache::new(4);
        panic!()
    }
}

#[cfg(all(test, feature="nightly"))]
mod benches {
    extern crate test;

    use self::test::{Bencher, black_box};
    use super::PoolCache;
    use std::sync::Mutex;

    // const MS_SLEEP_PER_OP: u32 = 1;

    lazy_static! {
        static ref POOL_1: Mutex<PoolCache> = Mutex::new(PoolCache::new(1));
        static ref POOL_2: Mutex<PoolCache> = Mutex::new(PoolCache::new(2));
        static ref POOL_3: Mutex<PoolCache> = Mutex::new(PoolCache::new(3));
        static ref POOL_4: Mutex<PoolCache> = Mutex::new(PoolCache::new(4));
        static ref POOL_5: Mutex<PoolCache> = Mutex::new(PoolCache::new(5));
        static ref POOL_8: Mutex<PoolCache> = Mutex::new(PoolCache::new(8));
    }

    fn fib(n: u64) -> u64 {
        let mut prev_prev: u64 = 1;
        let mut prev = 1;
        let mut current = 1;
        for _ in 2..(n+1) {
            current = prev_prev.wrapping_add(prev);
            prev_prev = prev;
            prev = current;
        }
        current
    }

    fn threads_interleaved_n(pool: &mut PoolCache)  {
        let size = 1024; // 1kiB

        let mut data = vec![1u8; size];
        pool.scope(|s| {
            for e in data.iter_mut() {
                s.execute(move || {
                    *e += fib(black_box(1000 * (*e as u64))) as u8;
                    //thread::sleep_ms(MS_SLEEP_PER_OP);
                });
            }
        });
    }

    #[bench]
    fn threads_interleaved_1(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_1.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_2(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_2.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_4(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_4.lock().unwrap()))
    }

    #[bench]
    fn threads_interleaved_8(b: &mut Bencher) {
        b.iter(|| threads_interleaved_n(&mut POOL_8.lock().unwrap()))
    }

    fn threads_chunked_n(pool: &mut PoolCache) {
        let size = 1024 * 1024 * 100 / 4; // 100MiB

        let n = pool.thread_count();
        let mut data = vec![0u32; size];
        pool.scope(|s| {
            let l = (data.len() - 1) / n as usize + 1;
            for es in data.chunks_mut(l) {
                s.execute(move || {
                    if es.len() > 1 {
                        es[0] = 1;
                        es[1] = 1;
                        for i in 2..es.len() {
                            // Fibonnaci gets big fast,
                            // so just wrap around all the time
                            es[i] = es[i-1].wrapping_add(es[i-2]);
                        }
                    }
                    //thread::sleep_ms(MS_SLEEP_PER_OP);
                });
            }
        });
    }

    #[bench]
    fn threads_chunked_1(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_1.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_2(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_2.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_3(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_3.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_4(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_4.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_5(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_5.lock().unwrap()))
    }

    #[bench]
    fn threads_chunked_8(b: &mut Bencher) {
        b.iter(|| threads_chunked_n(&mut POOL_8.lock().unwrap()))
    }
}
<|endoftext|>


<commit_before>#[test]
fn it_works() {
}
<commit_msg>Remove unused lib.rs<commit_after><|endoftext|>


<commit_before>extern crate rand;
extern crate time;
extern crate zoneinfo;

use time::{Timespec, Duration, at_utc};
use std::collections::BTreeMap;
use std::rc::Rc;
use rand::{Rng, thread_rng};
use zoneinfo::{ZoneInfo, ZoneInfoElement};
use std::io::Result;

/// Represents abstract action identifier
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct ScheduleContext(pub usize);

/// Represents a fixed moment in a day
#[derive(Debug)]
pub enum ScheduleTime {
    LocalTime(Duration),
    UtcTime(Duration)
}

// Local time definition
enum LocalTimeState {
    // Zoneinfo state is not loaded yet
    Unknown,
    // Current zoneinfo specifies no future daylight saving time change is expected
    NoChangePending(ZoneInfoElement),
    // Daylight saving time change is pending
    ChangePending(Timespec, // transistion time
                  ZoneInfoElement, // zone information before transisition time
                  ZoneInfoElement) // zone information at and after transition time
}

impl ScheduleTime {
    /// Create a moment in a day
    pub fn new(h:u8, m:u8, s:u8) -> ScheduleTime {
        ScheduleTime::LocalTime(
            Duration::hours(h as i64) +
            Duration::minutes(m as i64) +
            Duration::seconds(s as i64))
    }

    /// Create a moment in a day based on Timespec
    pub fn new_from_timespec(ts: Timespec) -> ScheduleTime {
        let mut tm_utc = at_utc(ts);

        tm_utc.tm_hour = 0;
        tm_utc.tm_min = 0;
        tm_utc.tm_sec = 0;
        tm_utc.tm_nsec = 0;

        ScheduleTime::UtcTime(ts - tm_utc.to_timespec())
    }

    /// Convert schedule time to actual time stamp
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Timespec {
        match self {
            // timestamp is simply a reference to UTC so just add the offset
            &ScheduleTime::UtcTime(offset) => ut_midnight_reference + offset,
            // timestamp is a reference to the moment in a day
            &ScheduleTime::LocalTime(offset) => {
                let ut_offset = match *localtime {
                    LocalTimeState::NoChangePending(ref info) => info.ut_offset,
                    LocalTimeState::ChangePending(transition_time, ref before, ref after) => {
                        if ut_midnight_reference < transition_time {
                            before.ut_offset
                        }
                        else {
                            after.ut_offset
                        }
                    }
                    _ => unreachable!()
                };

                ut_midnight_reference + offset + ut_offset
            }
        }
    }
}

impl std::fmt::Display for ScheduleTime {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        let duration = match self {
            &ScheduleTime::UtcTime(d) => d,
            &ScheduleTime::LocalTime(d) => d,
        };
        try!(write!(fmt, "{:02}:{:02}:{:02}", duration.num_hours(), duration.num_minutes() % 60, duration.num_seconds() % 60));
        if let &ScheduleTime::UtcTime(_) = self {
            try!(write!(fmt, " (UTC)"));
        }
        Ok(())
    }
}

/// Represent a (abstract) moment in a day
pub enum ScheduleMoment {
    Fixed(ScheduleTime),
    Fuzzy(ScheduleTime, ScheduleTime),
    ByClosure(Box<Fn(Timespec) -> ScheduleTime>, Duration)
}

impl std::fmt::Display for ScheduleMoment {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            &ScheduleMoment::Fixed(ref t) => write!(fmt, "{}", t),
            &ScheduleMoment::Fuzzy(ref b, ref a) => write!(fmt, "{} ~ {}", b, a),
            &ScheduleMoment::ByClosure(_, ref variance) =>
                write!(fmt, "dynamic ~{}s", variance.num_seconds()),
        }
    }
}

/// Represents a moment and an specific action in a day
struct ScheduleEvent<'a> {
    moment: ScheduleMoment,
    action: &'a ScheduleAction,
    context: ScheduleContext
}

impl <'a>ScheduleEvent<'a> {
    /// Determine timestamp for event
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Timespec {
        match self.moment {
            ScheduleMoment::Fixed(ref moment) =>
                moment.create_timestamp(ut_midnight_reference, localtime),
            ScheduleMoment::Fuzzy(ref m1, ref m2) => {
                // pick a time between both given moment
                let mut rng = rand::thread_rng();
                let t1 = m1.create_timestamp(ut_midnight_reference, localtime);
                let t2 = m2.create_timestamp(ut_midnight_reference, localtime);
                let t_start = if t1 >= t2 {t2} else {t1};
                let t_end = if t1 >= t2 {t1} else {t2};
                let duration = t_end - t_start;
                t_start + Duration::seconds(rng.gen_range(0, duration.num_seconds()))
            }
            ScheduleMoment::ByClosure(ref func, ref variance) => {
                let moment = func(ut_midnight_reference);
                // generate a offset based on variance compared to the generated moment
                let mut rng = rand::thread_rng();
                let offset = rng.gen_range(0, variance.num_seconds());
                let offset = Duration::seconds(variance.num_seconds() / 2 - offset);
                moment.create_timestamp(ut_midnight_reference, localtime) + offset
            }
        }
    }
}

pub trait ScheduleAction {
    /// Perform a action (in a day)
    fn kick(&self, timestamp: &Timespec, event: &ScheduleMoment, kick: &ScheduleContext);
}

/// Represents multiple moments in a day
pub struct Schedule<'a> {
    // List of (abstract) moments in a day
    events: Vec<Rc<ScheduleEvent<'a>>>,

    // Time zone related information
    zoneinfo: ZoneInfo,

    // Next zone change
    localtime: LocalTimeState,

    // Tree of actual scheduled moments and reference to the abstract moment in a day
    schedule: BTreeMap<Timespec, Rc<ScheduleEvent<'a>>>
}

impl <'a>Schedule<'a> {
    /// Create a (empty) list of moments in a day
    pub fn new() -> Result<Schedule<'a>> {
        Ok(Schedule {
            events: vec![],
            zoneinfo: try!(ZoneInfo::get_local_zoneinfo()),
            localtime: LocalTimeState::Unknown,
            schedule: BTreeMap::new()
        })
    }

    /// Add a (abstract) moment and action in a day
    pub fn add_event(&mut self,
                     moment: ScheduleMoment,
                     action: &'a ScheduleAction,
                     context: ScheduleContext) {
        self.events.push(Rc::new(ScheduleEvent {
            moment: moment,
            action: action,
            context: context
        }));
    }

    /// Determine next zone info state
    fn new_change_state(&self, timestamp: Timespec) -> LocalTimeState {
        // yes, a unwrap, since a serious problem be present when no zoneinfo could be retrieved
        let actual = self.zoneinfo.get_actual_zoneinfo(timestamp).unwrap();
        match self.zoneinfo.get_next_transition_time(timestamp) {
            Some((next_change, next)) =>
                LocalTimeState::ChangePending(next_change, actual, next),
            None => LocalTimeState::NoChangePending(actual)
        }
    }

    /// Update the schedule for 24 hours
    pub fn update_schedule(&mut self, ut_midnight_reference: Timespec) {
        match self.localtime {
            LocalTimeState::Unknown =>
                self.localtime = self.new_change_state(ut_midnight_reference),
            LocalTimeState::ChangePending(time, _, _) => {
                if time >= ut_midnight_reference {
                    self.localtime = self.new_change_state(ut_midnight_reference);
                }
            },
            _ => {}
        }

        for event in &self.events {
            let timestamp = event.create_timestamp(ut_midnight_reference, &self.localtime);
            self.schedule.insert(timestamp, event.clone());
        }
    }

    /// Consume schedule until now and kick last or current event and returns next event time
    pub fn kick_event(&mut self, now: Timespec) -> Option<Timespec> {
        let past_events: Vec<Timespec> = self.schedule.keys().filter(|&k| *k <= now).cloned().collect();

        // kick the current event...
        if let Some(timestamp) = past_events.last() {
            if let Some(schedule_event) = self.schedule.get(timestamp) {
                schedule_event.action.kick(&timestamp, &schedule_event.moment, &schedule_event.context);
            }
        }

        // ...and consume that and prior events
        for past_event in past_events {
            self.schedule.remove(&past_event);
        }

        self.schedule.keys().cloned().nth(0)
    }

    /// Peek when next event will happen
    pub fn peek_event(&self) -> Option<Timespec> {
        self.schedule.keys().cloned().nth(0)
    }

    // TODO remove
    pub fn print_keys(&self) {
        for k in self.schedule.keys() {
            println!("{} {}", at_utc(*k).rfc822(), self.schedule.get(k).unwrap().moment);
        }
    }
}
<commit_msg>Fixed wrong timezone calculations<commit_after>extern crate rand;
extern crate time;
extern crate zoneinfo;

use time::{Timespec, Duration, at_utc};
use std::collections::BTreeMap;
use std::rc::Rc;
use rand::{Rng, thread_rng};
use zoneinfo::{ZoneInfo, ZoneInfoElement};
use std::io::Result;

/// Represents abstract action identifier
#[derive(Debug, Clone, Eq, PartialEq, Ord, PartialOrd)]
pub struct ScheduleContext(pub usize);

/// Represents a fixed moment in a day
#[derive(Debug)]
pub enum ScheduleTime {
    LocalTime(Duration),
    UtcTime(Duration)
}

// Local time definition
enum LocalTimeState {
    // Zoneinfo state is not loaded yet
    Unknown,
    // Current zoneinfo specifies no future daylight saving time change is expected
    NoChangePending(ZoneInfoElement),
    // Daylight saving time change is pending
    ChangePending(Timespec, // transistion time
                  ZoneInfoElement, // zone information before transisition time
                  ZoneInfoElement) // zone information at and after transition time
}

impl ScheduleTime {
    /// Create a moment in a day
    pub fn new(h:u8, m:u8, s:u8) -> ScheduleTime {
        ScheduleTime::LocalTime(
            Duration::hours(h as i64) +
            Duration::minutes(m as i64) +
            Duration::seconds(s as i64))
    }

    /// Create a moment in a day based on Timespec
    pub fn new_from_timespec(ts: Timespec) -> ScheduleTime {
        let mut tm_utc = at_utc(ts);

        tm_utc.tm_hour = 0;
        tm_utc.tm_min = 0;
        tm_utc.tm_sec = 0;
        tm_utc.tm_nsec = 0;

        ScheduleTime::UtcTime(ts - tm_utc.to_timespec())
    }

    /// Convert schedule time to actual time stamp
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Timespec {
        match self {
            // timestamp is simply a reference to UTC so just add the offset
            &ScheduleTime::UtcTime(offset) => ut_midnight_reference + offset,
            // timestamp is a reference to the moment in a day
            &ScheduleTime::LocalTime(offset) => {
                let ut_offset = match *localtime {
                    LocalTimeState::NoChangePending(ref info) => info.ut_offset,
                    LocalTimeState::ChangePending(transition_time, ref before, ref after) => {
                        if ut_midnight_reference < transition_time {
                            before.ut_offset
                        }
                        else {
                            after.ut_offset
                        }
                    }
                    _ => unreachable!()
                };

                ut_midnight_reference + offset - ut_offset
            }
        }
    }
}

impl std::fmt::Display for ScheduleTime {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        let duration = match self {
            &ScheduleTime::UtcTime(d) => d,
            &ScheduleTime::LocalTime(d) => d,
        };
        try!(write!(fmt, "{:02}:{:02}:{:02}", duration.num_hours(), duration.num_minutes() % 60, duration.num_seconds() % 60));
        if let &ScheduleTime::UtcTime(_) = self {
            try!(write!(fmt, " (UTC)"));
        }
        Ok(())
    }
}

/// Represent a (abstract) moment in a day
pub enum ScheduleMoment {
    Fixed(ScheduleTime),
    Fuzzy(ScheduleTime, ScheduleTime),
    ByClosure(Box<Fn(Timespec) -> ScheduleTime>, Duration)
}

impl std::fmt::Display for ScheduleMoment {
    fn fmt(&self, fmt: &mut std::fmt::Formatter) -> std::fmt::Result {
        match self {
            &ScheduleMoment::Fixed(ref t) => write!(fmt, "{}", t),
            &ScheduleMoment::Fuzzy(ref b, ref a) => write!(fmt, "{} ~ {}", b, a),
            &ScheduleMoment::ByClosure(_, ref variance) =>
                write!(fmt, "dynamic ~{}s", variance.num_seconds()),
        }
    }
}

/// Represents a moment and an specific action in a day
struct ScheduleEvent<'a> {
    moment: ScheduleMoment,
    action: &'a ScheduleAction,
    context: ScheduleContext
}

impl <'a>ScheduleEvent<'a> {
    /// Determine timestamp for event
    fn create_timestamp(&self, ut_midnight_reference: Timespec,
                        localtime: &LocalTimeState) -> Timespec {
        match self.moment {
            ScheduleMoment::Fixed(ref moment) =>
                moment.create_timestamp(ut_midnight_reference, localtime),
            ScheduleMoment::Fuzzy(ref m1, ref m2) => {
                // pick a time between both given moment
                let mut rng = rand::thread_rng();
                let t1 = m1.create_timestamp(ut_midnight_reference, localtime);
                let t2 = m2.create_timestamp(ut_midnight_reference, localtime);
                let t_start = if t1 >= t2 {t2} else {t1};
                let t_end = if t1 >= t2 {t1} else {t2};
                let duration = t_end - t_start;
                t_start + Duration::seconds(rng.gen_range(0, duration.num_seconds()))
            }
            ScheduleMoment::ByClosure(ref func, ref variance) => {
                let moment = func(ut_midnight_reference);
                // generate a offset based on variance compared to the generated moment
                let mut rng = rand::thread_rng();
                let offset = rng.gen_range(0, variance.num_seconds());
                let offset = Duration::seconds(variance.num_seconds() / 2 - offset);
                moment.create_timestamp(ut_midnight_reference, localtime) + offset
            }
        }
    }
}

pub trait ScheduleAction {
    /// Perform a action (in a day)
    fn kick(&self, timestamp: &Timespec, event: &ScheduleMoment, kick: &ScheduleContext);
}

/// Represents multiple moments in a day
pub struct Schedule<'a> {
    // List of (abstract) moments in a day
    events: Vec<Rc<ScheduleEvent<'a>>>,

    // Time zone related information
    zoneinfo: ZoneInfo,

    // Next zone change
    localtime: LocalTimeState,

    // Tree of actual scheduled moments and reference to the abstract moment in a day
    schedule: BTreeMap<Timespec, Rc<ScheduleEvent<'a>>>
}

impl <'a>Schedule<'a> {
    /// Create a (empty) list of moments in a day
    pub fn new() -> Result<Schedule<'a>> {
        Ok(Schedule {
            events: vec![],
            zoneinfo: try!(ZoneInfo::get_local_zoneinfo()),
            localtime: LocalTimeState::Unknown,
            schedule: BTreeMap::new()
        })
    }

    /// Add a (abstract) moment and action in a day
    pub fn add_event(&mut self,
                     moment: ScheduleMoment,
                     action: &'a ScheduleAction,
                     context: ScheduleContext) {
        self.events.push(Rc::new(ScheduleEvent {
            moment: moment,
            action: action,
            context: context
        }));
    }

    /// Determine next zone info state
    fn new_change_state(&self, timestamp: Timespec) -> LocalTimeState {
        // yes, a unwrap, since a serious problem be present when no zoneinfo could be retrieved
        let actual = self.zoneinfo.get_actual_zoneinfo(timestamp).unwrap();
        match self.zoneinfo.get_next_transition_time(timestamp) {
            Some((next_change, next)) =>
                LocalTimeState::ChangePending(next_change, actual, next),
            None => LocalTimeState::NoChangePending(actual)
        }
    }

    /// Update the schedule for 24 hours
    pub fn update_schedule(&mut self, ut_midnight_reference: Timespec) {
        match self.localtime {
            LocalTimeState::Unknown =>
                self.localtime = self.new_change_state(ut_midnight_reference),
            LocalTimeState::ChangePending(time, _, _) => {
                if time >= ut_midnight_reference {
                    self.localtime = self.new_change_state(ut_midnight_reference);
                }
            },
            _ => {}
        }

        for event in &self.events {
            let timestamp = event.create_timestamp(ut_midnight_reference, &self.localtime);
            self.schedule.insert(timestamp, event.clone());
        }
    }

    /// Consume schedule until now and kick last or current event and returns next event time
    pub fn kick_event(&mut self, now: Timespec) -> Option<Timespec> {
        let past_events: Vec<Timespec> = self.schedule.keys().filter(|&k| *k <= now).cloned().collect();

        // kick the current event...
        if let Some(timestamp) = past_events.last() {
            if let Some(schedule_event) = self.schedule.get(timestamp) {
                schedule_event.action.kick(&timestamp, &schedule_event.moment, &schedule_event.context);
            }
        }

        // ...and consume that and prior events
        for past_event in past_events {
            self.schedule.remove(&past_event);
        }

        self.schedule.keys().cloned().nth(0)
    }

    /// Peek when next event will happen
    pub fn peek_event(&self) -> Option<Timespec> {
        self.schedule.keys().cloned().nth(0)
    }

    // TODO remove
    pub fn print_keys(&self) {
        for k in self.schedule.keys() {
            println!("{} {}", at_utc(*k).rfc822(), self.schedule.get(k).unwrap().moment);
        }
    }
}
<|endoftext|>


<commit_before>//! # Fixerio API Wrapper
//!
//! http://fixer.io
//!
//! ## Usage
//! Add the following to `Cargo.toml`:
//!
//! ```rust,ignore
//! [dependencies]
//! fixerio = "0.1.2"
//! ```
//!
//! Synchronous example:
//!
//! ```rust,no_run
//!
//! extern crate fixerio;
//!
//! use fixerio::{Config, Currency, SyncApi};
//!
//! fn main() {
//!     let api = SyncApi::new().expect("Error creating API");
//!
//!     let config = Config::new(Currency::USD);
//!     let rates = api.get(&config).expect("Error retrieving rates");
//!
//!     println!("{:?}", rates);
//! }
//! ```
//!
//! Asynchronous example:
//!
//! ```rust,no_run
//!
//! extern crate fixerio;
//! extern crate tokio_core;
//!
//! use fixerio::{Config, Currency, AsyncApi};
//! use tokio_core::reactor::Core;
//!
//! fn main() {
//!     let mut core = Core::new().expect("Error creating core");
//!     let handle = core.handle();
//!
//!     let api = AsyncApi::new(&handle);
//!
//!     let config = Config::new(Currency::USD);
//!     let work = api.get(&config);
//!
//!     println!("{:?}", core.run(work).expect("Error retrieving rates"));
//! }
//! ```

#[macro_use]
extern crate serde_derive;
extern crate hyper;
extern crate tokio_core;
extern crate futures;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate error_chain;

mod client;
mod exchange;
mod future;
mod errors;

pub use self::client::{AsyncApi, SyncApi, Config};
pub use self::exchange::{Currency, Exchange, Rates};
pub use self::future::FixerioFuture;
pub use self::errors::{Error, ErrorKind};<commit_msg>Update lib.rs<commit_after>//! # Fixerio API Wrapper
//!
//! http://fixer.io
//!
//! ## Usage
//! Add the following to `Cargo.toml`:
//!
//! ```rust,ignore
//! [dependencies]
//! fixerio = "0.1.3"
//! ```
//!
//! Synchronous example:
//!
//! ```rust,no_run
//!
//! extern crate fixerio;
//!
//! use fixerio::{Config, Currency, SyncApi};
//!
//! fn main() {
//!     let api = SyncApi::new().expect("Error creating API");
//!
//!     let config = Config::new(Currency::USD);
//!     let rates = api.get(&config).expect("Error retrieving rates");
//!
//!     println!("{:?}", rates);
//! }
//! ```
//!
//! Asynchronous example:
//!
//! ```rust,no_run
//!
//! extern crate fixerio;
//! extern crate tokio_core;
//!
//! use fixerio::{Config, Currency, AsyncApi};
//! use tokio_core::reactor::Core;
//!
//! fn main() {
//!     let mut core = Core::new().expect("Error creating core");
//!     let handle = core.handle();
//!
//!     let api = AsyncApi::new(&handle);
//!
//!     let config = Config::new(Currency::USD);
//!     let work = api.get(&config);
//!
//!     println!("{:?}", core.run(work).expect("Error retrieving rates"));
//! }
//! ```

#[macro_use]
extern crate serde_derive;
extern crate hyper;
extern crate tokio_core;
extern crate futures;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate error_chain;

mod client;
mod exchange;
mod future;
mod errors;

pub use self::client::{AsyncApi, SyncApi, Config};
pub use self::exchange::{Currency, Exchange, Rates};
pub use self::future::FixerioFuture;
pub use self::errors::{Error, ErrorKind};
<|endoftext|>


<commit_before>#[macro_use(bson, doc)]
extern crate bson;
extern crate mongodb;
extern crate chrono;

use std::result;
use mongodb::{Client, ThreadedClient};
use mongodb::cursor::Cursor;
use mongodb::db::ThreadedDatabase;
use mongodb::coll::options::{FindOptions, CursorType};
use chrono::{DateTime, UTC, TimeZone};

#[derive(Debug)]
pub enum OplogError {
    MissingField(bson::ValueAccessError),
    Database(mongodb::Error),
    UnknownOperation(String),
}

impl From<bson::ValueAccessError> for OplogError {
    fn from(original: bson::ValueAccessError) -> OplogError {
        OplogError::MissingField(original)
    }
}

impl From<mongodb::Error> for OplogError {
    fn from(original: mongodb::Error) -> OplogError {
        OplogError::Database(original)
    }
}

type Result<T> = result::Result<T, OplogError>;

pub struct Oplog {
    cursor: Cursor,
}

#[derive(PartialEq, Debug)]
pub enum Operation<'a> {
    Insert { id: i64, timestamp: DateTime<UTC>, namespace: &'a str, document: &'a bson::Document },
    Update { id: i64, namespace: &'a str },
    Delete { id: i64, namespace: &'a str },
    Command { id: i64, namespace: &'a str },
    Database { id: i64, namespace: &'a str },
    Noop { id: i64, timestamp: DateTime<UTC>, document: &'a bson::Document },
}

impl<'a> Operation<'a> {
    pub fn new(document: &'a bson::Document) -> Result<Operation<'a>> {
        let op = try!(document.get_str("op"));

        match op {
            "n" => document_to_noop(document),
            "i" => document_to_insert(document),
            _ => Err(OplogError::UnknownOperation(op.to_owned())),
        }
    }
}

fn document_to_noop(document: &bson::Document) -> Result<Operation> {
    let h = try!(document.get_i64("h"));
    let ts = try!(document.get_time_stamp("ts"));
    let o = try!(document.get_document("o"));

    Ok(Operation::Noop { id: h, timestamp: timestamp_to_datetime(ts), document: o })
}

fn document_to_insert(document: &bson::Document) -> Result<Operation> {
    let h = try!(document.get_i64("h"));
    let ts = try!(document.get_time_stamp("ts"));
    let o = try!(document.get_document("o"));
    let ns = try!(document.get_str("ns"));

    Ok(Operation::Insert { id: h, timestamp: timestamp_to_datetime(ts), document: o, namespace: ns })
}

fn timestamp_to_datetime(timestamp: i64) -> DateTime<UTC> {
    let seconds = timestamp >> 32;
    let nanoseconds = ((timestamp & 0xFFFFFFFF) * 1000000) as u32;

    UTC.timestamp(seconds, nanoseconds)
}

impl Iterator for Oplog {
    type Item = bson::Document;

    fn next(&mut self) -> Option<bson::Document> {
        loop {
            if let Some(Ok(op)) = self.cursor.next() {
                return Some(op);
            }
        }
    }
}

impl Oplog {
    pub fn new(client: Client) -> Result<Oplog> {
        let coll = client.db("local").collection("oplog.rs");

        let mut opts = FindOptions::new();
        opts.cursor_type = CursorType::TailableAwait;
        opts.no_cursor_timeout = true;

        Ok(Oplog { cursor: try!(coll.find(None, Some(opts))) })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use bson::Bson;
    use bson::oid::ObjectId;
    use chrono::{UTC, TimeZone};

    #[test]
    fn operation_converts_noops() {
        let ref doc = doc! {
            "ts" => (Bson::TimeStamp(1479419535 << 32)),
            "h" => (-2135725856567446411i64),
            "v" => 2,
            "op" => "n",
            "ns" => "",
            "o" => {
                "msg" => "initiating set"
            }
        };
        let operation = Operation::new(doc).unwrap();

        match operation {
            Operation::Noop { id, timestamp, document } => {
                assert_eq!(-2135725856567446411i64, id);
                assert_eq!(UTC.timestamp(1479419535, 0), timestamp);
                assert_eq!("initiating set", document.get_str("msg").unwrap());
            },
            _ => panic!("Unexpected type of operation"),
        }
    }

    #[test]
    fn operation_converts_inserts() {
        let oid = ObjectId::with_string("583050b26813716e505a5bf2").unwrap();
        let ref doc = doc! {
            "ts" => (Bson::TimeStamp(1479561394 << 32)),
            "h" => (-1742072865587022793i64),
            "v" => 2,
            "op" => "i",
            "ns" => "foo.bar",
            "o" => {
                "_id" => (Bson::ObjectId(oid)),
                "foo" => "bar"
            }
        };
        let operation = Operation::new(doc).unwrap();

        match operation {
            Operation::Insert { id, timestamp, namespace, document } => {
                assert_eq!(-1742072865587022793i64, id);
                assert_eq!(UTC.timestamp(1479561394, 0), timestamp);
                assert_eq!("foo.bar", namespace);
                assert_eq!("bar", document.get_str("foo").unwrap());
            },
            _ => panic!("Unexpected type of operation"),
        }
    }
}
<commit_msg>Implement all operation types<commit_after>#[macro_use(bson, doc)]
extern crate bson;
extern crate mongodb;
extern crate chrono;

use std::fmt;
use std::result;
use mongodb::{Client, ThreadedClient};
use mongodb::cursor::Cursor;
use mongodb::db::ThreadedDatabase;
use mongodb::coll::options::{FindOptions, CursorType};
use chrono::{DateTime, UTC, TimeZone};

#[derive(Debug)]
pub enum Error {
    MissingField(bson::ValueAccessError),
    Database(mongodb::Error),
    UnknownOperation(String),
}

impl From<bson::ValueAccessError> for Error {
    fn from(original: bson::ValueAccessError) -> Error {
        Error::MissingField(original)
    }
}

impl From<mongodb::Error> for Error {
    fn from(original: mongodb::Error) -> Error {
        Error::Database(original)
    }
}

type Result<T> = result::Result<T, Error>;

#[derive(PartialEq, Debug)]
pub enum Operation {
    Noop { id: i64, timestamp: DateTime<UTC>, message: String },
    Insert { id: i64, timestamp: DateTime<UTC>, namespace: String, document: bson::Document },
    Update { id: i64, timestamp: DateTime<UTC>, namespace: String, query: bson::Document, update: bson::Document },
    Delete { id: i64, timestamp: DateTime<UTC>, namespace: String, query: bson::Document },
    Command { id: i64, timestamp: DateTime<UTC>, namespace: String, command: bson::Document },
}

impl Operation {
    pub fn new(document: &bson::Document) -> Result<Operation> {
        let op = try!(document.get_str("op"));

        match op {
            "n" => noop(document),
            "i" => insert(document),
            "u" => update(document),
            "d" => delete(document),
            "c" => command(document),
            _ => Err(Error::UnknownOperation(op.to_owned())),
        }
    }
}

impl fmt::Display for Operation {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Operation::Noop { id, timestamp, ref message } => {
                write!(f, "No-op #{} at {}: {}", id, timestamp, message)
            },
            Operation::Insert { id, timestamp, ref namespace, ref document } => {
                write!(f, "Insert #{} into {} at {}: {}", id, namespace, timestamp, document)
            },
            Operation::Update { id, timestamp, ref namespace, ref query, ref update } => {
                write!(f, "Update #{} {} with {} at {}: {}", id, namespace, query, timestamp, update)
            },
            Operation::Delete { id, timestamp, ref namespace, ref query } => {
                write!(f, "Delete #{} from {} at {}: {}", id, namespace, timestamp, query)
            },
            Operation::Command { id, timestamp, ref namespace, ref command } => {
                write!(f, "Command #{} {} at {}: {}", id, namespace, timestamp, command)
            }
        }
    }
}

fn noop(document: &bson::Document) -> Result<Operation> {
    let h = try!(document.get_i64("h"));
    let ts = try!(document.get_time_stamp("ts"));
    let o = try!(document.get_document("o"));
    let msg = try!(o.get_str("msg"));

    Ok(Operation::Noop { id: h, timestamp: timestamp_to_datetime(ts), message: msg.to_owned() })
}

fn insert(document: &bson::Document) -> Result<Operation> {
    let h = try!(document.get_i64("h"));
    let ts = try!(document.get_time_stamp("ts"));
    let ns = try!(document.get_str("ns"));
    let o = try!(document.get_document("o"));

    Ok(Operation::Insert { id: h, timestamp: timestamp_to_datetime(ts), namespace: ns.to_owned(), document: o.to_owned() })
}

fn update(document: &bson::Document) -> Result<Operation> {
    let h = try!(document.get_i64("h"));
    let ts = try!(document.get_time_stamp("ts"));
    let ns = try!(document.get_str("ns"));
    let o = try!(document.get_document("o"));
    let o2 = try!(document.get_document("o2"));

    Ok(Operation::Update { id: h, timestamp: timestamp_to_datetime(ts), namespace: ns.to_owned(), query: o2.to_owned(), update: o.to_owned() })
}

fn delete(document: &bson::Document) -> Result<Operation> {
    let h = try!(document.get_i64("h"));
    let ts = try!(document.get_time_stamp("ts"));
    let ns = try!(document.get_str("ns"));
    let o = try!(document.get_document("o"));

    Ok(Operation::Delete { id: h, timestamp: timestamp_to_datetime(ts), namespace: ns.to_owned(), query: o.to_owned() })
}

fn command(document: &bson::Document) -> Result<Operation> {
    let h = try!(document.get_i64("h"));
    let ts = try!(document.get_time_stamp("ts"));
    let ns = try!(document.get_str("ns"));
    let o = try!(document.get_document("o"));

    Ok(Operation::Command { id: h, timestamp: timestamp_to_datetime(ts), namespace: ns.to_owned(), command: o.to_owned() })
}

fn timestamp_to_datetime(timestamp: i64) -> DateTime<UTC> {
    let seconds = timestamp >> 32;
    let nanoseconds = ((timestamp & 0xFFFFFFFF) * 1000000) as u32;

    UTC.timestamp(seconds, nanoseconds)
}

pub struct Oplog {
    cursor: Cursor,
}

impl Iterator for Oplog {
    type Item = Operation;

    fn next(&mut self) -> Option<Self::Item> {
        loop {
            match self.cursor.next() {
                Some(Ok(document)) => return Operation::new(&document).ok(),
                _ => continue,
            }
        }
    }
}

impl Oplog {
    pub fn new(client: Client) -> Result<Oplog> {
        let coll = client.db("local").collection("oplog.rs");

        let mut opts = FindOptions::new();
        opts.cursor_type = CursorType::TailableAwait;
        opts.no_cursor_timeout = true;

        Ok(Oplog { cursor: try!(coll.find(None, Some(opts))) })
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use bson::Bson;
    use bson::oid::ObjectId;
    use chrono::{UTC, TimeZone};

    #[test]
    fn operation_converts_noops() {
        let doc = doc! {
            "ts" => (Bson::TimeStamp(1479419535 << 32)),
            "h" => (-2135725856567446411i64),
            "v" => 2,
            "op" => "n",
            "ns" => "",
            "o" => {
                "msg" => "initiating set"
            }
        };

        match Operation::new(&doc) {
            Ok(Operation::Noop { id, timestamp, message }) => {
                assert_eq!(-2135725856567446411i64, id);
                assert_eq!(UTC.timestamp(1479419535, 0), timestamp);
                assert_eq!("initiating set", message);
            },
            _ => panic!("Unexpected operation"),
        }
    }

    #[test]
    fn operation_converts_inserts() {
        let oid = ObjectId::with_string("583050b26813716e505a5bf2").unwrap();
        let doc = doc! {
            "ts" => (Bson::TimeStamp(1479561394 << 32)),
            "h" => (-1742072865587022793i64),
            "v" => 2,
            "op" => "i",
            "ns" => "foo.bar",
            "o" => {
                "_id" => (Bson::ObjectId(oid)),
                "foo" => "bar"
            }
        };

        match Operation::new(&doc) {
            Ok(Operation::Insert { id, timestamp, namespace, document }) => {
                assert_eq!(-1742072865587022793i64, id);
                assert_eq!(UTC.timestamp(1479561394, 0), timestamp);
                assert_eq!("foo.bar", namespace);
                assert_eq!("bar", document.get_str("foo").expect("foo missing"));
            },
            _ => panic!("Unexpected type of operation"),
        }
    }

    #[test]
    fn operation_converts_updates() {
        let oid = ObjectId::with_string("583033a3643431ab5be6ec35").unwrap();
        let doc = doc! {
            "ts" => (Bson::TimeStamp(1479561033 << 32)),
            "h" => (3511341713062188019i64),
            "v" => 2,
            "op" => "u",
            "ns" => "foo.bar",
            "o2" => {
                "_id" => (Bson::ObjectId(oid))
            },
            "o" => {
                "$set" => {
                    "foo" => "baz"
                }
            }
        };

        match Operation::new(&doc) {
            Ok(Operation::Update { id, timestamp, namespace, query, update }) => {
                assert_eq!(3511341713062188019i64, id);
                assert_eq!(UTC.timestamp(1479561033, 0), timestamp);
                assert_eq!("foo.bar", namespace);
                assert_eq!(ObjectId::with_string("583033a3643431ab5be6ec35").unwrap(), query.get_object_id("_id").expect("_id missing").to_owned());
                assert_eq!("baz", update.get_document("$set").and_then(|o| o.get_str("foo")).unwrap());
            },
            _ => panic!("Unexpected type of operation"),
        }
    }

    #[test]
    fn operation_converts_deletes() {
        let oid = ObjectId::with_string("582e287cfedf6fb051b2efdf").unwrap();
        let doc = doc! {
            "ts" => (Bson::TimeStamp(1479421186 << 32)),
            "h" => (-5457382347563537847i64),
            "v" => 2,
            "op" => "d",
            "ns" => "foo.bar",
            "o" => {
                "_id" => (Bson::ObjectId(oid))
            }
        };

        match Operation::new(&doc) {
            Ok(Operation::Delete { id, timestamp, namespace, query }) => {
                assert_eq!(-5457382347563537847i64, id);
                assert_eq!(UTC.timestamp(1479421186, 0), timestamp);
                assert_eq!("foo.bar", namespace);
                assert_eq!(ObjectId::with_string("582e287cfedf6fb051b2efdf").unwrap(), query.get_object_id("_id").expect("_id missing").to_owned());
            },
            _ => panic!("Unexpected type of operation"),
        }
    }

    #[test]
    fn operation_converts_commands() {
        let doc = doc! {
            "ts" => (Bson::TimeStamp(1479553955 << 32)),
            "h" => (-7222343681970774929i64),
            "v" => 2,
            "op" => "c",
            "ns" => "test.$cmd",
            "o" => {
                "create" => "foo"
            }
        };

        match Operation::new(&doc) {
            Ok(Operation::Command { id, timestamp, namespace, command }) => {
                assert_eq!(-7222343681970774929i64, id);
                assert_eq!(UTC.timestamp(1479553955, 0), timestamp);
                assert_eq!("test.$cmd", namespace);
                assert_eq!("foo", command.get_str("create").expect("create missing"));
            },
            _ => panic!("Unexpected type of operation"),
        }
    }
}
<|endoftext|>


<commit_before>//! oppgave - A simple Redis-based task queue
//!
//!
//! ## Example: Producer
//!
//! ```rust,ignore
//! #[derive(RustcDecodable, RustcEncodable)]
//! struct Job { id: u64 }
//!
//! let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! let con = client.get_connection().unwrap();
//! let producer = Queue::new("default".into(), con);
//!
//! producer.push(Job{ id: 42 });
//! ```
//!
//! ## Example: Worker
//!
//! ```rust,ignore
//! #[derive(RustcDecodable, RustcEncodable)]
//! struct Job { id: u64 }
//!
//! let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! let con = client.get_connection().unwrap();
//! let worker = Queue::new("default".into(), con);
//!
//! while let Some(task) = worker.next() {
//!     println!("Working with Job {}", job.id);
//! }
//! ```

#![deny(missing_docs)]

extern crate rustc_serialize;
extern crate redis;
extern crate libc;

use std::{str, thread};
use std::cell::Cell;
use std::ops::{Deref, Drop};
use std::convert::From;
use rustc_serialize::{json, Decodable, Encodable};
use redis::{Value, RedisResult, ErrorKind, Commands};

/// Return the PID of the calling process.
/// TODO: Does this work on Windows?
fn getpid() -> i32 {
    unsafe { libc::getpid() as i32 }
}

/// Task objects that can be reconstructed from the JSON stored in Redis
///
/// Implemented for all `Decodable` objects by default.
pub trait TaskDecodable where Self: Sized {
    /// Decode the given Redis value into a task
    ///
    /// This should decode the string value into a proper task.
    /// The string value is encoded as JSON.
    fn decode_task(value: &Value) -> RedisResult<Self>;
}

/// Task objects that can be encoded to JSON to be stored in Redis
///
/// Implemented for all `Encodable` objects by default.
pub trait TaskEncodable {
    /// Encode the value into a Blob to insert into Redis
    ///
    /// It should encode the value into a JSON string.
    fn encode_task(&self) -> Vec<u8>;
}

impl<T: Decodable> TaskDecodable for T {
    fn decode_task(value: &Value) -> RedisResult<T> {
        match value {
            &Value::Data(ref v) => {
                let s = try!(str::from_utf8(&v));
                Ok(try!(json::decode(&s).map_err(|_| (ErrorKind::TypeError, "JSON decode failed"))))

            },
            v @ _ => {
                println!("what do we have here: {:?}", v);
                try!(Err((ErrorKind::TypeError, "Can only decode from a string")))
            }
        }
    }
}

impl<T: Encodable> TaskEncodable for T {
    fn encode_task(&self) -> Vec<u8> {
        json::encode(self).unwrap().into_bytes()
    }
}

/// A wrapper of the fetched task
///
/// Pops a task from the backup on drop.
pub struct TaskGuard<'a, T: 'a> {
    task: T,
    queue: &'a Queue
}

impl<'a, T> TaskGuard<'a, T> {
    /// Stop the underlying queue
    pub fn stop(&self) {
        self.queue.stop();
    }

    /// Get access to the underlying task
    pub fn inner(&self) -> &T {
        &self.task
    }

    /// Get access to the wrapper queue
    pub fn queue(&self) -> &Queue {
        self.queue
    }
}

impl<'a, T> Deref for TaskGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.task
    }
}

impl<'a, T> Drop for TaskGuard<'a, T> {
    fn drop(&mut self) {
        // Pop job from backup queue
        let backup = &self.queue.backup_queue[..];
        let _ : () = self.queue.client.lpop(backup).expect("LPOP from backup queue failed");
    }
}

/// A Queue allows to push new tasks or wait for them
///
/// Allows for reliable job processing.
///
/// On fetch jobs are moved to a backup queue.
/// They are popped from the backup queue, when the returned task guard is dropped.
///
/// ## Example
///
/// ```rust,ignore
/// #[derive(RustcDecodable, RustcEncodable)]
/// struct Job { id: u64 }
///
/// let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// let con = client.get_connection().unwrap();
/// let producer = Queue::new("default".into(), con);
///
/// producer.push(Job{ id: 42 });
/// ```
pub struct Queue {
    queue_name: String,
    backup_queue: String,
    stopped: Cell<bool>,
    client: redis::Connection,
}

impl Queue {
    /// Create a new Queue for the given name
    pub fn new(name: String, client: redis::Connection) -> Queue {
        let qname = format!("oppgave:{}", name);
        let backup_queue = format!("{}:{}:{}",
                                   qname,
                                   getpid(),
                                   thread::current().name().unwrap_or("default".into()));

        Queue {
            queue_name: qname,
            backup_queue: backup_queue,
            client: client,
            stopped: Cell::new(false),
        }
    }

    /// Stop processing the queue
    ///
    /// On the next `.next()` call `None` will be returned.
    pub fn stop(&self) {
        self.stopped.set(true);
    }

    /// Check if queue processing is stopped
    pub fn is_stopped(&self) -> bool {
        self.stopped.get()
    }

    /// Get the full queue name
    pub fn queue(&self) -> &str {
        &self.queue_name
    }

    /// Get the full backup queue name
    pub fn backup_queue(&self) -> &str {
        &self.backup_queue
    }

    /// Get the number of remaining tasks in the queue
    pub fn size(&self) -> u64 {
        self.client.llen(self.queue()).unwrap_or(0)
    }

    /// Push a new task to the queue
    pub fn push<T: TaskEncodable>(&self, task: T) {
        let _ : () = self.client.lpush(self.queue(), task.encode_task()).expect("LPUSH failed to enqueue task");
    }

    /// Grab the next task from the queue
    ///
    /// This method blocks and waits until a new task is available.
    pub fn next<T: TaskDecodable>(&self) -> Option<RedisResult<TaskGuard<T>>> {
        if self.stopped.get() {
            return None;
        }

        let v;
        {
            let qname = &self.queue_name[..];
            let backup = &self.backup_queue[..];

            v = match self.client.brpoplpush(qname, backup, 0) {
                Ok(v) => v,
                Err(_) => {
                    return Some(Err(From::from((ErrorKind::TypeError, "next failed"))));
                }
            };
        }

        let v = match v {
            v @ Value::Data(_) => v,
            _ => {
                return Some(Err(From::from((ErrorKind::TypeError, "Not a proper reply"))));
            }
        };

        let task = T::decode_task(&v).unwrap();

        Some(Ok(TaskGuard{task: task, queue: self}))
    }
}

#[cfg(test)]
mod test {
    extern crate redis;

    use redis::Commands;
    use super::*;

    #[derive(RustcDecodable, RustcEncodable)]
    struct Job {
        id: u64
    }

    #[test]
    fn decodes_job() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();
        let worker = Queue::new("default".into(), con2);

        let _ : () = con.rpush(worker.queue(), "{\"id\":42}").unwrap();

        let j = worker.next::<Job>().unwrap().unwrap();
        assert_eq!(42, j.id);
    }

    #[test]
    fn releases_job() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();
        let worker = Queue::new("default".into(), con2);
        let bqueue = worker.backup_queue();

        let _ : () = con.del(bqueue).unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":42}").unwrap();

        {
            let j = worker.next::<Job>().unwrap().unwrap();
            assert_eq!(42, j.id);
            let in_backup : Vec<String> = con.lrange(bqueue, 0, -1).unwrap();
            assert_eq!(1, in_backup.len());
            assert_eq!("{\"id\":42}", in_backup[0]);
        }

        let in_backup : u32 = con.llen(bqueue).unwrap();
        assert_eq!(0, in_backup);
    }

    #[test]
    fn can_be_stopped() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();
        let worker = Queue::new("stopper".into(), con2);

        let _ : () = con.del(worker.queue()).unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":1}").unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":2}").unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":3}").unwrap();

        assert_eq!(3, worker.size());

        while let Some(task) = worker.next::<Job>() {
            let task = task.unwrap();
            task.stop();
        }

        assert_eq!(2, worker.size());
    }

    #[test]
    fn can_enqueu() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();

        let worker = Queue::new("enqueue".into(), con2);
        let _ : () = con.del(worker.queue()).unwrap();

        assert_eq!(0, worker.size());

        worker.push(Job{id: 53});

        assert_eq!(1, worker.size());

        let j = worker.next::<Job>().unwrap().unwrap();
        assert_eq!(53, j.id);
    }
}
<commit_msg>feat: Force failure of task<commit_after>//! oppgave - A simple Redis-based task queue
//!
//!
//! ## Example: Producer
//!
//! ```rust,ignore
//! #[derive(RustcDecodable, RustcEncodable)]
//! struct Job { id: u64 }
//!
//! let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! let con = client.get_connection().unwrap();
//! let producer = Queue::new("default".into(), con);
//!
//! producer.push(Job{ id: 42 });
//! ```
//!
//! ## Example: Worker
//!
//! ```rust,ignore
//! #[derive(RustcDecodable, RustcEncodable)]
//! struct Job { id: u64 }
//!
//! let client = redis::Client::open("redis://127.0.0.1/").unwrap();
//! let con = client.get_connection().unwrap();
//! let worker = Queue::new("default".into(), con);
//!
//! while let Some(task) = worker.next() {
//!     println!("Working with Job {}", job.id);
//! }
//! ```

#![deny(missing_docs)]

extern crate rustc_serialize;
extern crate redis;
extern crate libc;

use std::{str, thread};
use std::cell::Cell;
use std::ops::{Deref, Drop};
use std::convert::From;
use rustc_serialize::{json, Decodable, Encodable};
use redis::{Value, RedisResult, ErrorKind, Commands};

/// Return the PID of the calling process.
/// TODO: Does this work on Windows?
fn getpid() -> i32 {
    unsafe { libc::getpid() as i32 }
}

/// Task objects that can be reconstructed from the JSON stored in Redis
///
/// Implemented for all `Decodable` objects by default.
pub trait TaskDecodable where Self: Sized {
    /// Decode the given Redis value into a task
    ///
    /// This should decode the string value into a proper task.
    /// The string value is encoded as JSON.
    fn decode_task(value: &Value) -> RedisResult<Self>;
}

/// Task objects that can be encoded to JSON to be stored in Redis
///
/// Implemented for all `Encodable` objects by default.
pub trait TaskEncodable {
    /// Encode the value into a Blob to insert into Redis
    ///
    /// It should encode the value into a JSON string.
    fn encode_task(&self) -> Vec<u8>;
}

impl<T: Decodable> TaskDecodable for T {
    fn decode_task(value: &Value) -> RedisResult<T> {
        match value {
            &Value::Data(ref v) => {
                let s = try!(str::from_utf8(&v));
                Ok(try!(json::decode(&s).map_err(|_| (ErrorKind::TypeError, "JSON decode failed"))))

            },
            v @ _ => {
                println!("what do we have here: {:?}", v);
                try!(Err((ErrorKind::TypeError, "Can only decode from a string")))
            }
        }
    }
}

impl<T: Encodable> TaskEncodable for T {
    fn encode_task(&self) -> Vec<u8> {
        json::encode(self).unwrap().into_bytes()
    }
}

/// A wrapper of the fetched task
///
/// Pops a task from the backup on drop.
pub struct TaskGuard<'a, T: 'a> {
    task: T,
    queue: &'a Queue,
    failed: Cell<bool>,
}

impl<'a, T> TaskGuard<'a, T> {
    /// Stop the underlying queue
    pub fn stop(&self) {
        self.queue.stop();
    }

    /// Fail the current task and don't remove it from the backup queue
    pub fn fail(&self) {
        self.failed.set(true);
    }

    /// Get access to the underlying task
    pub fn inner(&self) -> &T {
        &self.task
    }

    /// Get access to the wrapper queue
    pub fn queue(&self) -> &Queue {
        self.queue
    }
}

impl<'a, T> Deref for TaskGuard<'a, T> {
    type Target = T;

    fn deref(&self) -> &T {
        &self.task
    }
}

impl<'a, T> Drop for TaskGuard<'a, T> {
    fn drop(&mut self) {
        if !self.failed.get() {
            // Pop job from backup queue
            let backup = &self.queue.backup_queue[..];
            let _ : () = self.queue.client.lpop(backup).expect("LPOP from backup queue failed");
        }
    }
}

/// A Queue allows to push new tasks or wait for them
///
/// Allows for reliable job processing.
///
/// On fetch jobs are moved to a backup queue.
/// They are popped from the backup queue, when the returned task guard is dropped.
///
/// ## Example
///
/// ```rust,ignore
/// #[derive(RustcDecodable, RustcEncodable)]
/// struct Job { id: u64 }
///
/// let client = redis::Client::open("redis://127.0.0.1/").unwrap();
/// let con = client.get_connection().unwrap();
/// let producer = Queue::new("default".into(), con);
///
/// producer.push(Job{ id: 42 });
/// ```
pub struct Queue {
    queue_name: String,
    backup_queue: String,
    stopped: Cell<bool>,
    client: redis::Connection,
}

impl Queue {
    /// Create a new Queue for the given name
    pub fn new(name: String, client: redis::Connection) -> Queue {
        let qname = format!("oppgave:{}", name);
        let backup_queue = format!("{}:{}:{}",
                                   qname,
                                   getpid(),
                                   thread::current().name().unwrap_or("default".into()));

        Queue {
            queue_name: qname,
            backup_queue: backup_queue,
            client: client,
            stopped: Cell::new(false),
        }
    }

    /// Stop processing the queue
    ///
    /// On the next `.next()` call `None` will be returned.
    pub fn stop(&self) {
        self.stopped.set(true);
    }

    /// Check if queue processing is stopped
    pub fn is_stopped(&self) -> bool {
        self.stopped.get()
    }

    /// Get the full queue name
    pub fn queue(&self) -> &str {
        &self.queue_name
    }

    /// Get the full backup queue name
    pub fn backup_queue(&self) -> &str {
        &self.backup_queue
    }

    /// Get the number of remaining tasks in the queue
    pub fn size(&self) -> u64 {
        self.client.llen(self.queue()).unwrap_or(0)
    }

    /// Push a new task to the queue
    pub fn push<T: TaskEncodable>(&self, task: T) {
        let _ : () = self.client.lpush(self.queue(), task.encode_task()).expect("LPUSH failed to enqueue task");
    }

    /// Grab the next task from the queue
    ///
    /// This method blocks and waits until a new task is available.
    pub fn next<T: TaskDecodable>(&self) -> Option<RedisResult<TaskGuard<T>>> {
        if self.stopped.get() {
            return None;
        }

        let v;
        {
            let qname = &self.queue_name[..];
            let backup = &self.backup_queue[..];

            v = match self.client.brpoplpush(qname, backup, 0) {
                Ok(v) => v,
                Err(_) => {
                    return Some(Err(From::from((ErrorKind::TypeError, "next failed"))));
                }
            };
        }

        let v = match v {
            v @ Value::Data(_) => v,
            _ => {
                return Some(Err(From::from((ErrorKind::TypeError, "Not a proper reply"))));
            }
        };

        let task = T::decode_task(&v).unwrap();

        Some(Ok(TaskGuard{task: task, queue: self, failed: Cell::new(false)}))
    }
}

#[cfg(test)]
mod test {
    extern crate redis;

    use redis::Commands;
    use super::*;

    #[derive(RustcDecodable, RustcEncodable)]
    struct Job {
        id: u64
    }

    #[test]
    fn decodes_job() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();
        let worker = Queue::new("default".into(), con2);

        let _ : () = con.rpush(worker.queue(), "{\"id\":42}").unwrap();

        let j = worker.next::<Job>().unwrap().unwrap();
        assert_eq!(42, j.id);
    }

    #[test]
    fn releases_job() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();
        let worker = Queue::new("default".into(), con2);
        let bqueue = worker.backup_queue();

        let _ : () = con.del(bqueue).unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":42}").unwrap();

        {
            let j = worker.next::<Job>().unwrap().unwrap();
            assert_eq!(42, j.id);
            let in_backup : Vec<String> = con.lrange(bqueue, 0, -1).unwrap();
            assert_eq!(1, in_backup.len());
            assert_eq!("{\"id\":42}", in_backup[0]);
        }

        let in_backup : u32 = con.llen(bqueue).unwrap();
        assert_eq!(0, in_backup);
    }

    #[test]
    fn can_be_stopped() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();
        let worker = Queue::new("stopper".into(), con2);

        let _ : () = con.del(worker.queue()).unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":1}").unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":2}").unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":3}").unwrap();

        assert_eq!(3, worker.size());

        while let Some(task) = worker.next::<Job>() {
            let task = task.unwrap();
            task.stop();
        }

        assert_eq!(2, worker.size());
    }

    #[test]
    fn can_enqueue() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();

        let worker = Queue::new("enqueue".into(), con2);
        let _ : () = con.del(worker.queue()).unwrap();

        assert_eq!(0, worker.size());

        worker.push(Job{id: 53});

        assert_eq!(1, worker.size());

        let j = worker.next::<Job>().unwrap().unwrap();
        assert_eq!(53, j.id);
    }

    #[test]
    fn does_not_drop_failed() {
        let client = redis::Client::open("redis://127.0.0.1/").unwrap();
        let con = client.get_connection().unwrap();
        let con2 = client.get_connection().unwrap();
        let worker = Queue::new("failure".into(), con2);

        let _ : () = con.del(worker.queue()).unwrap();
        let _ : () = con.del(worker.backup_queue()).unwrap();
        let _ : () = con.lpush(worker.queue(), "{\"id\":1}").unwrap();

        {
            let task : TaskGuard<Job> = worker.next().unwrap().unwrap();
            task.fail();
        }

        let len : u32 = con.llen(worker.backup_queue()).unwrap();
        assert_eq!(1, len);
    }
}
<|endoftext|>