<commit_before>//! oppgave - A simple Redis-based task queue
//!
//! oppgave provides a small reliable queue on top of Redis.
//! It allows to push tasks and fetch them again.
//!
//! Tasks can be arbitrary objects, as long as they can be encoded and decoded into a String.
//! The easiest way is to rely on JSON encoding by marking the task as `RustcEncodable` and
//! `RustcDecodable`.
//!
//! Oppgave prodives a [reliable queue](http://redis.io/commands/rpoplpush#pattern-reliable-queue)
//! by moving acquired tasks to a backup queue.
//! If a task finished it is removed from this backup queue.
//! If a task fails it remains in the backup queue for human processing later on.
//!
//! See [`Queue`](struct.Queue.html) for a detailed documentation how to use this.
//!
//! The following examples are provided as executables as well:
//!
//! ## 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);
//! }
//! ```

#![cfg_attr(feature = "clippy", allow(unstable_features))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![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 data stored in Redis
///
/// Implemented for all `Decodable` objects by default by relying on JSON encoding.
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 a string to be stored in Redis
///
/// Implemented for all `Encodable` objects by default by encoding as JSON.
pub trait TaskEncodable {
    /// Encode the value into a Blob to insert into Redis
    ///
    /// It should encode the value into a 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));
                json::decode(&s).map_err(|_| From::from((ErrorKind::TypeError, "JSON decode failed")))

            },
            _ => {
                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.
///
/// If not marked otherwise, the contained task will be removed from the backup queue on `Drop`.
/// Call `fail()` to mark the processing as failed. The task will remain in the backup queue.
///
/// It derefs to the underlying task automatically for all other method calls.
pub struct TaskGuard<'a, T: 'a> {
    task: T,
    queue: &'a Queue,
    failed: Cell<bool>,
}

impl<'a, T> TaskGuard<'a, T> {
    /// Fail the current task, in order to keep it in the backup queue.
    pub fn fail(&self) {
        self.failed.set(true);
    }

    /// Get access to the underlying task.
    ///
    /// This should only be needed in very few cases, as this guard derefs automatically.
    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 fetch and decode them for processing.
///
/// ## Push
///
/// Pushing new tasks to the queue encodes the given object and stores it in Redis for later
/// processing.
///
/// ## 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 });
/// ```
///
///
/// ## Fetch
///
/// A Queue provides a convenient `Iterator`-like interface over tasks:
///
/// ```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 queue = Queue::new("default".into(), con);
///
/// while let Some(task) = queue.next() {
///     println!("Working with Job {}", job.id);
/// }
/// ```
///
/// Fetching a task from a queue returns a wrapper object, which delegates to the underlying,
/// automatically decoded task object.
/// If this wrapper object is dropped, the task is considered complete and therefore removed from
/// the queue.
/// If the task processing fails, you need to call `fail()` on this wrapper.
///
/// ### Example: Task complete & task failed
///
/// ```rust,ignore
/// 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);
///
/// {
///   // `next` gives an Option<Result<...>>
///   let task = worker.next().unwrap().unwrap();
/// } // Task succeeded, removed from backup queue
///
/// {
///   let task = worker.next().unwrap().unwrap();
///   task.fail();
/// } // Task failed, stays in backup queue
/// ```
///
///
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) -> RedisResult<()> {
        self.client.lpush(self.queue(), task.encode_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"))));
            }
        };

        match T::decode_task(&v) {
            Err(e) => Some(Err(e)),
            Ok(task) => Some(Ok(TaskGuard{task: task, queue: self, failed: Cell::new(false)}))
        }
    }
}

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

    use redis::Commands;
    use super::{Queue, TaskGuard};

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

    #[test]
    fn decodes_job() {
        let client = redis::Client::open("redis://127.0.0.1:6380/").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:6380/").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:6380/").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();
            worker.stop();
        }

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

    #[test]
    fn can_enqueue() {
        let client = redis::Client::open("redis://127.0.0.1:6380/").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}).unwrap();

        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:6380/").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);
    }
}
<commit_msg>style: Use method-annotation instead of ignored variable<commit_after>//! oppgave - A simple Redis-based task queue
//!
//! oppgave provides a small reliable queue on top of Redis.
//! It allows to push tasks and fetch them again.
//!
//! Tasks can be arbitrary objects, as long as they can be encoded and decoded into a String.
//! The easiest way is to rely on JSON encoding by marking the task as `RustcEncodable` and
//! `RustcDecodable`.
//!
//! Oppgave prodives a [reliable queue](http://redis.io/commands/rpoplpush#pattern-reliable-queue)
//! by moving acquired tasks to a backup queue.
//! If a task finished it is removed from this backup queue.
//! If a task fails it remains in the backup queue for human processing later on.
//!
//! See [`Queue`](struct.Queue.html) for a detailed documentation how to use this.
//!
//! The following examples are provided as executables as well:
//!
//! ## 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);
//! }
//! ```

#![cfg_attr(feature = "clippy", allow(unstable_features))]
#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]
#![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 data stored in Redis
///
/// Implemented for all `Decodable` objects by default by relying on JSON encoding.
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 a string to be stored in Redis
///
/// Implemented for all `Encodable` objects by default by encoding as JSON.
pub trait TaskEncodable {
    /// Encode the value into a Blob to insert into Redis
    ///
    /// It should encode the value into a 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));
                json::decode(&s).map_err(|_| From::from((ErrorKind::TypeError, "JSON decode failed")))

            },
            _ => {
                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.
///
/// If not marked otherwise, the contained task will be removed from the backup queue on `Drop`.
/// Call `fail()` to mark the processing as failed. The task will remain in the backup queue.
///
/// It derefs to the underlying task automatically for all other method calls.
pub struct TaskGuard<'a, T: 'a> {
    task: T,
    queue: &'a Queue,
    failed: Cell<bool>,
}

impl<'a, T> TaskGuard<'a, T> {
    /// Fail the current task, in order to keep it in the backup queue.
    pub fn fail(&self) {
        self.failed.set(true);
    }

    /// Get access to the underlying task.
    ///
    /// This should only be needed in very few cases, as this guard derefs automatically.
    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[..];
            self.queue.client.lpop::<_, ()>(backup).expect("LPOP from backup queue failed");
        }
    }
}

/// A Queue allows to push new tasks or fetch and decode them for processing.
///
/// ## Push
///
/// Pushing new tasks to the queue encodes the given object and stores it in Redis for later
/// processing.
///
/// ## 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 });
/// ```
///
///
/// ## Fetch
///
/// A Queue provides a convenient `Iterator`-like interface over tasks:
///
/// ```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 queue = Queue::new("default".into(), con);
///
/// while let Some(task) = queue.next() {
///     println!("Working with Job {}", job.id);
/// }
/// ```
///
/// Fetching a task from a queue returns a wrapper object, which delegates to the underlying,
/// automatically decoded task object.
/// If this wrapper object is dropped, the task is considered complete and therefore removed from
/// the queue.
/// If the task processing fails, you need to call `fail()` on this wrapper.
///
/// ### Example: Task complete & task failed
///
/// ```rust,ignore
/// 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);
///
/// {
///   // `next` gives an Option<Result<...>>
///   let task = worker.next().unwrap().unwrap();
/// } // Task succeeded, removed from backup queue
///
/// {
///   let task = worker.next().unwrap().unwrap();
///   task.fail();
/// } // Task failed, stays in backup queue
/// ```
///
///
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) -> RedisResult<()> {
        self.client.lpush(self.queue(), task.encode_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"))));
            }
        };

        match T::decode_task(&v) {
            Err(e) => Some(Err(e)),
            Ok(task) => Some(Ok(TaskGuard{task: task, queue: self, failed: Cell::new(false)}))
        }
    }
}

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

    use redis::Commands;
    use super::{Queue, TaskGuard};

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

    #[test]
    fn decodes_job() {
        let client = redis::Client::open("redis://127.0.0.1:6380/").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:6380/").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:6380/").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();
            worker.stop();
        }

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

    #[test]
    fn can_enqueue() {
        let client = redis::Client::open("redis://127.0.0.1:6380/").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}).unwrap();

        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:6380/").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|>


<commit_before>//! A runtime for writing reliable, asynchronous, and slim applications.
//!
//! Tokio is an event-driven, non-blocking I/O platform for writing asynchronous
//! applications with the Rust programming language. At a high level, it
//! provides a few major components:
//!
//! * A multi threaded, work-stealing based task [scheduler][runtime].
//! * A [reactor] backed by the operating system's event queue (epoll, kqueue,
//!   IOCP, etc...).
//! * Asynchronous [TCP and UDP][net] sockets.
//! * Asynchronous [filesystem][fs] operations.
//! * [Timer][timer] API for scheduling work in the future.
//!
//! Tokio is built using [futures] as the abstraction for managing the
//! complexity of asynchronous programming.
//!
//! Guide level documentation is found on the [website].
//!
//! [website]: https://tokio.rs/docs/getting-started/hello-world/
//! [futures]: http://docs.rs/futures/0.1
//!
//! # Examples
//!
//! A simple TCP echo server:
//!
//! ```no_run
//! extern crate tokio;
//!
//! use tokio::prelude::*;
//! use tokio::io::copy;
//! use tokio::net::TcpListener;
//!
//! fn main() {
//!     // Bind the server's socket.
//!     let addr = "127.0.0.1:12345".parse().unwrap();
//!     let listener = TcpListener::bind(&addr)
//!         .expect("unable to bind TCP listener");
//!
//!     // Pull out a stream of sockets for incoming connections
//!     let server = listener.incoming()
//!         .map_err(|e| eprintln!("accept failed = {:?}", e))
//!         .for_each(|sock| {
//!             // Split up the reading and writing parts of the
//!             // socket.
//!             let (reader, writer) = sock.split();
//!
//!             // A future that echos the data and returns how
//!             // many bytes were copied...
//!             let bytes_copied = copy(reader, writer);
//!
//!             // ... after which we'll print what happened.
//!             let handle_conn = bytes_copied.map(|amt| {
//!                 println!("wrote {:?} bytes", amt)
//!             }).map_err(|err| {
//!                 eprintln!("IO error {:?}", err)
//!             });
//!
//!             // Spawn the future as a concurrent task.
//!             tokio::spawn(handle_conn)
//!         });
//!
//!     // Start the Tokio runtime
//!     tokio::run(server);
//! }
//! ```

#![doc(html_root_url = "https://docs.rs/tokio/0.1.5")]
#![deny(missing_docs, warnings, missing_debug_implementations)]

#[macro_use]
extern crate futures;
extern crate mio;
extern crate tokio_current_thread;
extern crate tokio_io;
extern crate tokio_executor;
extern crate tokio_codec;
extern crate tokio_fs;
extern crate tokio_reactor;
extern crate tokio_threadpool;
extern crate tokio_timer;
extern crate tokio_tcp;
extern crate tokio_udp;

pub mod clock;
pub mod executor;
pub mod fs;
pub mod net;
pub mod reactor;
pub mod runtime;
pub mod timer;
pub mod util;

pub use executor::spawn;
pub use runtime::run;

pub mod codec {
    //! Utilities for encoding and decoding frames.
    //!
    //! Contains adapters to go from streams of bytes, [`AsyncRead`] and
    //! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
    //! Framed streams are also known as [transports].
    //!
    //! [`AsyncRead`]: #
    //! [`AsyncWrite`]: #
    //! [`Sink`]: #
    //! [`Stream`]: #
    //! [transports]: #

    pub use tokio_codec::{
        Decoder,
        Encoder,
        Framed,
        FramedParts,
        FramedRead,
        FramedWrite,
    };
}

pub mod io {
    //! Asynchronous I/O.
    //!
    //! This module is the asynchronous version of `std::io`. Primarily, it
    //! defines two traits, [`AsyncRead`] and [`AsyncWrite`], which extend the
    //! `Read` and `Write` traits of the standard library.
    //!
    //! # AsyncRead and AsyncWrite
    //!
    //! [`AsyncRead`] and [`AsyncWrite`] must only be implemented for
    //! non-blocking I/O types that integrate with the futures type system. In
    //! other words, these types must never block the thread, and instead the
    //! current task is notified when the I/O resource is ready.
    //!
    //! # Standard input and output
    //!
    //! Tokio provides asynchronous APIs to standard [input], [output], and [error].
    //! These APIs are very similar to the ones provided by `std`, but they also
    //! implement [`AsyncRead`] and [`AsyncWrite`].
    //!
    //! Unlike *most* other Tokio APIs, the standard input / output APIs
    //! **must** be used from the context of the Tokio runtime as they require
    //! Tokio specific features to function.
    //!
    //! [input]: fn.stdin.html
    //! [output]: fn.stdout.html
    //! [error]: fn.stderr.html
    //!
    //! # Utility functions
    //!
    //! Utilities functions are provided for working with [`AsyncRead`] /
    //! [`AsyncWrite`] types. For example, [`copy`] asynchronously copies all
    //! data from a source to a destination.
    //!
    //! # `std` re-exports
    //!
    //! Additionally, [`Read`], [`Write`], [`Error`], [`ErrorKind`], and
    //! [`Result`] are re-exported from `std::io` for ease of use.
    //!
    //! [`AsyncRead`]: trait.AsyncRead.html
    //! [`AsyncWrite`]: trait.AsyncWrite.html
    //! [`copy`]: fn.copy.html
    //! [`Read`]: trait.Read.html
    //! [`Write`]: trait.Write.html
    //! [`Error`]: struct.Error.html
    //! [`ErrorKind`]: enum.ErrorKind.html
    //! [`Result`]: type.Result.html

    pub use tokio_io::{
        AsyncRead,
        AsyncWrite,
    };

    // standard input, output, and error
    pub use tokio_fs::{
        stdin,
        Stdin,
        stdout,
        Stdout,
        stderr,
        Stderr,
    };

    // Utils
    pub use tokio_io::io::{
        copy,
        Copy,
        flush,
        Flush,
        lines,
        Lines,
        read_exact,
        ReadExact,
        read_to_end,
        ReadToEnd,
        read_until,
        ReadUntil,
        ReadHalf,
        shutdown,
        Shutdown,
        write_all,
        WriteAll,
        WriteHalf,
    };

    // Re-export io::Error so that users don't have to deal
    // with conflicts when `use`ing `futures::io` and `std::io`.
    pub use ::std::io::{
        Error,
        ErrorKind,
        Result,
        Read,
        Write,
    };
}

pub mod prelude {
    //! A "prelude" for users of the `tokio` crate.
    //!
    //! This prelude is similar to the standard library's prelude in that you'll
    //! almost always want to import its entire contents, but unlike the standard
    //! library's prelude you'll have to do so manually:
    //!
    //! ```
    //! use tokio::prelude::*;
    //! ```
    //!
    //! The prelude may grow over time as additional items see ubiquitous use.

    pub use tokio_io::{
        AsyncRead,
        AsyncWrite,
    };

    pub use util::{
        FutureExt,
    };

    pub use ::std::io::{
        Read,
        Write,
    };

    pub use futures::{
        Future,
        future,
        Stream,
        stream,
        Sink,
        IntoFuture,
        Async,
        AsyncSink,
        Poll,
        task,
    };
}
<commit_msg>tokio::codec docs + additional exports (#546)<commit_after>//! A runtime for writing reliable, asynchronous, and slim applications.
//!
//! Tokio is an event-driven, non-blocking I/O platform for writing asynchronous
//! applications with the Rust programming language. At a high level, it
//! provides a few major components:
//!
//! * A multi threaded, work-stealing based task [scheduler][runtime].
//! * A [reactor] backed by the operating system's event queue (epoll, kqueue,
//!   IOCP, etc...).
//! * Asynchronous [TCP and UDP][net] sockets.
//! * Asynchronous [filesystem][fs] operations.
//! * [Timer][timer] API for scheduling work in the future.
//!
//! Tokio is built using [futures] as the abstraction for managing the
//! complexity of asynchronous programming.
//!
//! Guide level documentation is found on the [website].
//!
//! [website]: https://tokio.rs/docs/getting-started/hello-world/
//! [futures]: http://docs.rs/futures/0.1
//!
//! # Examples
//!
//! A simple TCP echo server:
//!
//! ```no_run
//! extern crate tokio;
//!
//! use tokio::prelude::*;
//! use tokio::io::copy;
//! use tokio::net::TcpListener;
//!
//! fn main() {
//!     // Bind the server's socket.
//!     let addr = "127.0.0.1:12345".parse().unwrap();
//!     let listener = TcpListener::bind(&addr)
//!         .expect("unable to bind TCP listener");
//!
//!     // Pull out a stream of sockets for incoming connections
//!     let server = listener.incoming()
//!         .map_err(|e| eprintln!("accept failed = {:?}", e))
//!         .for_each(|sock| {
//!             // Split up the reading and writing parts of the
//!             // socket.
//!             let (reader, writer) = sock.split();
//!
//!             // A future that echos the data and returns how
//!             // many bytes were copied...
//!             let bytes_copied = copy(reader, writer);
//!
//!             // ... after which we'll print what happened.
//!             let handle_conn = bytes_copied.map(|amt| {
//!                 println!("wrote {:?} bytes", amt)
//!             }).map_err(|err| {
//!                 eprintln!("IO error {:?}", err)
//!             });
//!
//!             // Spawn the future as a concurrent task.
//!             tokio::spawn(handle_conn)
//!         });
//!
//!     // Start the Tokio runtime
//!     tokio::run(server);
//! }
//! ```

#![doc(html_root_url = "https://docs.rs/tokio/0.1.5")]
#![deny(missing_docs, warnings, missing_debug_implementations)]

#[macro_use]
extern crate futures;
extern crate mio;
extern crate tokio_current_thread;
extern crate tokio_io;
extern crate tokio_executor;
extern crate tokio_codec;
extern crate tokio_fs;
extern crate tokio_reactor;
extern crate tokio_threadpool;
extern crate tokio_timer;
extern crate tokio_tcp;
extern crate tokio_udp;

pub mod clock;
pub mod executor;
pub mod fs;
pub mod net;
pub mod reactor;
pub mod runtime;
pub mod timer;
pub mod util;

pub use executor::spawn;
pub use runtime::run;

pub mod codec {
    //! Utilities for encoding and decoding frames.
    //!
    //! Contains adapters to go from streams of bytes, [`AsyncRead`] and
    //! [`AsyncWrite`], to framed streams implementing [`Sink`] and [`Stream`].
    //! Framed streams are also known as [transports].
    //!
    //! [`AsyncRead`]: ../io/trait.AsyncRead.html
    //! [`AsyncWrite`]: ../io/trait.AsyncWrite.html
    //! [`Sink`]: https://docs.rs/futures/0.1/futures/sink/trait.Sink.html
    //! [`Stream`]: https://docs.rs/futures/0.1/futures/stream/trait.Stream.html
    //! [transports]: https://tokio.rs/docs/going-deeper/frames/

    pub use tokio_codec::{
        Decoder,
        Encoder,
        Framed,
        FramedParts,
        FramedRead,
        FramedWrite,
        BytesCodec,
        LinesCodec,
    };
}

pub mod io {
    //! Asynchronous I/O.
    //!
    //! This module is the asynchronous version of `std::io`. Primarily, it
    //! defines two traits, [`AsyncRead`] and [`AsyncWrite`], which extend the
    //! `Read` and `Write` traits of the standard library.
    //!
    //! # AsyncRead and AsyncWrite
    //!
    //! [`AsyncRead`] and [`AsyncWrite`] must only be implemented for
    //! non-blocking I/O types that integrate with the futures type system. In
    //! other words, these types must never block the thread, and instead the
    //! current task is notified when the I/O resource is ready.
    //!
    //! # Standard input and output
    //!
    //! Tokio provides asynchronous APIs to standard [input], [output], and [error].
    //! These APIs are very similar to the ones provided by `std`, but they also
    //! implement [`AsyncRead`] and [`AsyncWrite`].
    //!
    //! Unlike *most* other Tokio APIs, the standard input / output APIs
    //! **must** be used from the context of the Tokio runtime as they require
    //! Tokio specific features to function.
    //!
    //! [input]: fn.stdin.html
    //! [output]: fn.stdout.html
    //! [error]: fn.stderr.html
    //!
    //! # Utility functions
    //!
    //! Utilities functions are provided for working with [`AsyncRead`] /
    //! [`AsyncWrite`] types. For example, [`copy`] asynchronously copies all
    //! data from a source to a destination.
    //!
    //! # `std` re-exports
    //!
    //! Additionally, [`Read`], [`Write`], [`Error`], [`ErrorKind`], and
    //! [`Result`] are re-exported from `std::io` for ease of use.
    //!
    //! [`AsyncRead`]: trait.AsyncRead.html
    //! [`AsyncWrite`]: trait.AsyncWrite.html
    //! [`copy`]: fn.copy.html
    //! [`Read`]: trait.Read.html
    //! [`Write`]: trait.Write.html
    //! [`Error`]: struct.Error.html
    //! [`ErrorKind`]: enum.ErrorKind.html
    //! [`Result`]: type.Result.html

    pub use tokio_io::{
        AsyncRead,
        AsyncWrite,
    };

    // standard input, output, and error
    pub use tokio_fs::{
        stdin,
        Stdin,
        stdout,
        Stdout,
        stderr,
        Stderr,
    };

    // Utils
    pub use tokio_io::io::{
        copy,
        Copy,
        flush,
        Flush,
        lines,
        Lines,
        read_exact,
        ReadExact,
        read_to_end,
        ReadToEnd,
        read_until,
        ReadUntil,
        ReadHalf,
        shutdown,
        Shutdown,
        write_all,
        WriteAll,
        WriteHalf,
    };

    // Re-export io::Error so that users don't have to deal
    // with conflicts when `use`ing `futures::io` and `std::io`.
    pub use ::std::io::{
        Error,
        ErrorKind,
        Result,
        Read,
        Write,
    };
}

pub mod prelude {
    //! A "prelude" for users of the `tokio` crate.
    //!
    //! This prelude is similar to the standard library's prelude in that you'll
    //! almost always want to import its entire contents, but unlike the standard
    //! library's prelude you'll have to do so manually:
    //!
    //! ```
    //! use tokio::prelude::*;
    //! ```
    //!
    //! The prelude may grow over time as additional items see ubiquitous use.

    pub use tokio_io::{
        AsyncRead,
        AsyncWrite,
    };

    pub use util::{
        FutureExt,
    };

    pub use ::std::io::{
        Read,
        Write,
    };

    pub use futures::{
        Future,
        future,
        Stream,
        stream,
        Sink,
        IntoFuture,
        Async,
        AsyncSink,
        Poll,
        task,
    };
}
<|endoftext|>


<commit_before>#![crate_id = "iron"]
#![comment = "Rapid Web Development in Rust"]
#![license = "MIT"]

#![deny(missing_doc)]
#![deny(unused_result)]
#![deny(unnecessary_qualification)]
#![deny(non_camel_case_types)]
#![deny(unused_variable)]
#![deny(deprecated_owned_vector)]
#![deny(unnecessary_typecast)]

#![feature(macro_rules, phase)]

//! The main crate for the Iron library.

<commit_msg>(feat) Added iron module.<commit_after>#![crate_id = "iron"]
#![comment = "Rapid Web Development in Rust"]
#![license = "MIT"]

//#![deny(missing_doc)]
#![deny(unused_result)]
#![deny(unnecessary_qualification)]
#![deny(non_camel_case_types)]
#![deny(unused_variable)]
#![deny(deprecated_owned_vector)]
#![deny(unnecessary_typecast)]

#![feature(macro_rules, phase)]
//! The main crate for the Iron library.

extern crate http;

pub mod iron;
<|endoftext|>


<commit_before>//
// yaml.rs
// Nothing to see here yet
//

#![crate_id="yaml"]

use std::iter::Iterator;
use std::option::Option;

pub struct Scanner<'a> {
    reader: Box<Reader + 'a>,
}

impl <'a>Scanner<'a> {
    pub fn new<T: Reader + 'a>(reader: Box<T>) -> Scanner<'a> {
        Scanner { reader: reader }
    }
}

pub enum Token {
    Character,
}

impl <'a>Iterator for Scanner<'a> {

    // TODO: add Item

    pub fn next(&mut self) -> Option<Token> {
        Option::Some(Token::Character)
    }
}

mod test {

    use std::io::MemReader;
    use std::vec;

    use super::Scanner;

    #[test]
    fn test_scanner() {
        let stream = "---";
        let mut reader = MemReader::new(stream.bytes().collect());
        let mut scanner = Scanner::new(Box::new(reader));
        for token in scanner.iter() {
            println!("{}", token);
        }
    }
}
<commit_msg>Implement a token iterator<commit_after>//
// yaml.rs
// Nothing to see here yet
//

#![crate_id="yaml"]

use std::iter::Iterator;
use std::option::Option;

pub struct Scanner<'a> {
    reader: Box<Reader + 'a>,
}

impl <'a>Scanner<'a> {
    pub fn new<T: Reader + 'a>(reader: Box<T>) -> Scanner<'a> {
        Scanner { reader: reader }
    }
}

#[derive(PartialEq, Copy, Show)]
pub enum Token {
    Character,
    Other,
}

impl <'a>Iterator for Scanner<'a> {

    type Item = Token;

    fn next(&mut self) -> Option<Token> {
        Option::Some(Token::Character)
    }
}

mod test {

    use std::io::MemReader;
    use std::vec;

    use super::{Scanner, Token};

    #[test]
    fn test_scanner() {
        let stream = "---";
        let mut reader = MemReader::new(stream.bytes().collect());
        let mut scanner = Scanner::new(Box::new(reader));
        for token in scanner.take(5) {
            assert!(token == Token::Character);
        }
    }
}
<|endoftext|>


<commit_before>use Core;
use addons;

pub struct Mcu
{
    core: Core,
    addons: Vec<Box<addons::Addon>>,
}

impl Mcu
{
    pub fn new(core: Core) -> Self {
        Mcu {
            core: core,
            addons: Vec::new(),
        }
    }

    pub fn attach(&mut self, addon: Box<addons::Addon>) {
        self.addons.push(addon);
    }

    pub fn tick(&mut self) {
        self.core.tick();

        for addon in self.addons.iter_mut() {
            addon.tick(&mut self.core);
        }
    }

    pub fn core(&self) -> &Core { &self.core }
}
<commit_msg>Make Mcu::core public<commit_after>use Core;
use addons;

pub struct Mcu
{
    pub core: Core,
    addons: Vec<Box<addons::Addon>>,
}

impl Mcu
{
    pub fn new(core: Core) -> Self {
        Mcu {
            core: core,
            addons: Vec::new(),
        }
    }

    pub fn attach(&mut self, addon: Box<addons::Addon>) {
        self.addons.push(addon);
    }

    pub fn tick(&mut self) {
        self.core.tick();

        for addon in self.addons.iter_mut() {
            addon.tick(&mut self.core);
        }
    }
}
<|endoftext|>


<commit_before>use std::fs::File;
use std::io::Write;
use std::process::{Command, Stdio};

use hyper::Client;
use rusoto::ChainProvider;
use serde_json::from_slice;
use tempdir::TempDir;

use encryption::Encryptor;
use error::{KawsError, KawsResult};

pub struct Certificate(Vec<u8>);

pub struct CertificateAuthority {
    cert: Certificate,
    key: PrivateKey,
}

pub struct CertificateSigningRequest(Vec<u8>);

pub struct PrivateKey(Vec<u8>);

#[derive(Deserialize)]
struct CfsslGencertResponse {
    cert: Vec<u8>,
    key: Vec<u8>,
}

#[derive(Deserialize)]
struct CfsslSignResponse {
    cert: Vec<u8>,
}

impl Certificate {
    pub fn write_to_file(&self, file_path: &str) -> KawsResult {
        let mut file = File::create(file_path)?;
        file.write_all(self.as_bytes())?;

        Ok(None)
    }

    pub fn as_bytes(&self) -> &[u8] {
        &self.0
    }
}

impl From<Vec<u8>> for Certificate {
    fn from(vec: Vec<u8>) -> Self {
        Certificate(vec)
    }
}

impl CertificateAuthority {
    pub fn generate(common_name: &str) -> Result<Self, KawsError> {
        let mut command = Command::new("cfssl");

        command.args(&[
            "gencert",
            "-initca",
            "-",
        ]);

        command.stdin(Stdio::piped());
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        let mut child = command.spawn()?;

        match child.stdin.as_mut() {
            Some(stdin) => {
                stdin.write_all(
                    &format!(
                        r#"{{"CN":"{}","key":{{"algo":"rsa","size":2048}}}}}}"#,
                        common_name
                    ).as_bytes()
                )?;
            }
            None => {
                return Err(
                    KawsError::new("failed to acquire handle to stdin of child process".to_owned())
                );
            }
        }

        let output = child.wait_with_output()?;

        if output.status.success() {
            let raw: CfsslGencertResponse = from_slice(&output.stdout)?;
            Ok(raw.into())
        } else {
            Err(KawsError::new("Execution of `cfssl genkey` failed.".to_owned()))
        }
    }

    pub fn generate_cert(&self, common_name: &str, san: Option<&[&str]>)
    -> Result<(Certificate, PrivateKey), KawsError> {
        let (tempdir, cert_path, key_path) = self.temporary_write()?;

        let mut command = Command::new("cfssl");

        command.args(&[
            "gencert",
            "-ca",
            &cert_path,
            "-ca-key",
            &key_path,
            "-",
        ]);

        if let Some(san) = san {
            command.args(&[
                "-hostname",
                &san.join(","),
            ]);
        }

        command.arg("-");

        command.stdin(Stdio::piped());
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        let mut child = command.spawn()?;

        match child.stdin.as_mut() {
            Some(stdin) => {
                stdin.write_all(
                    &format!(
                        r#"{{"CN":"{}","key":{{"algo":"rsa","size":2048}}}}}}"#,
                        common_name
                    ).as_bytes()
                )?;
            }
            None => {
                return Err(
                    KawsError::new("failed to acquire handle to stdin of child process".to_owned())
                );
            }
        }

        let output = child.wait_with_output()?;

        let result = if output.status.success() {
            let raw: CfsslGencertResponse = from_slice(&output.stdout)?;

            Ok((raw.cert.into(), raw.key.into()))
        } else {
            Err(KawsError::new("Execution of `cfssl gencert` failed.".to_owned()))
        };

        tempdir.close()?;

        result
    }

    pub fn sign(&self, csr: &CertificateSigningRequest) -> Result<Certificate, KawsError> {
        let (tempdir, cert_path, key_path) = self.temporary_write()?;

        let mut command = Command::new("cfssl");

        command.args(&[
            "sign",
            "-ca",
            &cert_path,
            "-ca-key",
            &key_path,
            "-"
        ]);

        command.stdin(Stdio::piped());
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        let mut child = command.spawn()?;

        match child.stdin.as_mut() {
            Some(stdin) => {
                stdin.write_all(csr.as_bytes())?;
            }
            None => {
                return Err(
                    KawsError::new("failed to acquire handle to stdin of child process".to_owned())
                );
            }
        }

        let output = child.wait_with_output()?;

        let result = if output.status.success() {
            let response: CfsslSignResponse = from_slice(&output.stdout)?;

            Ok(response.cert.into())
        } else {
            Err(KawsError::new("Execution of `cfssl gencert` failed.".to_owned()))
        };

        tempdir.close()?;

        result
    }

    pub fn write_to_files(
        &self,
        encryptor: &mut Encryptor<ChainProvider, Client>,
        cert_file_path: &str,
        key_file_path: &str,
    ) -> KawsResult {
        let mut cert_file = File::create(cert_file_path)?;
        cert_file.write_all(self.as_bytes())?;

        encryptor.encrypt_and_write_file(self.key.as_bytes(), key_file_path)?;

        Ok(None)
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.cert.as_bytes()
    }

    // Private

    fn temporary_write(&self) -> Result<(TempDir, String, String), KawsError> {
        let tempdir = TempDir::new("kaws")?;

        let cert_path = tempdir.path().join("cert.pem");
        let key_path = tempdir.path().join("key.pem");
        let cert_path_string = match cert_path.to_str() {
            Some(value) => value.to_owned(),
            None => return Err(KawsError::new("Temporary path was invalid UTF-8".to_owned())),
        };
        let key_path_string = match key_path.to_str() {
            Some(value) => value.to_owned(),
            None => return Err(KawsError::new("Temporary path was invalid UTF-8".to_owned())),
        };
        let mut cert_file = File::create(cert_path)?;
        let mut key_file = File::create(key_path)?;
        cert_file.write_all(self.cert.as_bytes())?;
        key_file.write_all(self.key.as_bytes())?;

        Ok((tempdir, cert_path_string, key_path_string))
    }

}

impl From<CfsslGencertResponse> for CertificateAuthority {
    fn from(raw: CfsslGencertResponse) -> Self {
        CertificateAuthority {
            cert: raw.cert.into(),
            key: raw.key.into(),
        }
    }
}

impl CertificateSigningRequest {
    pub fn generate(common_name: &str, private_key: &PrivateKey) -> Result<Self, KawsError> {
        let mut command = Command::new("openssl");

        command.args(&[
            "req",
            "-new",
            "-key",
            "/dev/stdin",
            "-subj",
            &format!("/CN={}", common_name),
        ]);

        command.stdin(Stdio::piped());
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        let mut child = command.spawn()?;

        match child.stdin.as_mut() {
            Some(stdin) => {
                stdin.write_all(private_key.as_bytes())?;
            }
            None => {
                return Err(
                    KawsError::new("failed to acquire handle to stdin of child process".to_owned())
                );
            }
        };

        let output = child.wait_with_output()?;

        if output.status.success() {
            Ok(CertificateSigningRequest(output.stdout))
        } else {
            Err(KawsError::new("Execution of `openssl req` failed.".to_owned()))
        }
    }

    pub fn as_bytes(&self) -> &[u8] {
        &self.0
    }
}

impl From<Vec<u8>> for CertificateSigningRequest {
    fn from(vec: Vec<u8>) -> Self {
        CertificateSigningRequest(vec)
    }
}

impl PrivateKey {
    pub fn as_bytes(&self) -> &[u8] {
        &self.0
    }

    pub fn write_to_file(
        &self,
        encryptor: &mut Encryptor<ChainProvider, Client>,
        file_path: &str,
    ) -> KawsResult {
        encryptor.encrypt_and_write_file(self.as_bytes(), file_path)?;

        Ok(None)
    }
}

impl From<Vec<u8>> for PrivateKey {
    fn from(vec: Vec<u8>) -> Self {
        PrivateKey(vec)
    }
}
<commit_msg>Convert CSR generation to use cfssl.<commit_after>use std::fs::File;
use std::io::Write;
use std::process::{Command, Stdio};

use hyper::Client;
use rusoto::ChainProvider;
use serde_json::from_slice;
use tempdir::TempDir;

use encryption::Encryptor;
use error::{KawsError, KawsResult};

pub struct Certificate(Vec<u8>);

pub struct CertificateAuthority {
    cert: Certificate,
    key: PrivateKey,
}

pub struct CertificateSigningRequest(Vec<u8>);

pub struct PrivateKey(Vec<u8>);

#[derive(Deserialize)]
struct CfsslGencertResponse {
    cert: Vec<u8>,
    key: Vec<u8>,
}

#[derive(Deserialize)]
struct CfsslSignResponse {
    cert: Vec<u8>,
}

#[derive(Deserialize)]
struct CfsslGenkeyResponse {
    csr: Vec<u8>,
    key: Vec<u8>,
}

impl Certificate {
    pub fn write_to_file(&self, file_path: &str) -> KawsResult {
        let mut file = File::create(file_path)?;
        file.write_all(self.as_bytes())?;

        Ok(None)
    }

    pub fn as_bytes(&self) -> &[u8] {
        &self.0
    }
}

impl From<Vec<u8>> for Certificate {
    fn from(vec: Vec<u8>) -> Self {
        Certificate(vec)
    }
}

impl CertificateAuthority {
    pub fn generate(common_name: &str) -> Result<Self, KawsError> {
        let mut command = Command::new("cfssl");

        command.args(&[
            "gencert",
            "-initca",
            "-",
        ]);

        command.stdin(Stdio::piped());
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        let mut child = command.spawn()?;

        match child.stdin.as_mut() {
            Some(stdin) => {
                stdin.write_all(
                    format!(
                        r#"{{"CN":"{}","key":{{"algo":"rsa","size":2048}}}}}}"#,
                        common_name
                    ).as_bytes()
                )?;
            }
            None => {
                return Err(
                    KawsError::new("failed to acquire handle to stdin of child process".to_owned())
                );
            }
        }

        let output = child.wait_with_output()?;

        if output.status.success() {
            let raw: CfsslGencertResponse = from_slice(&output.stdout)?;

            Ok(raw.into())
        } else {
            Err(KawsError::new("Execution of `cfssl genkey` failed.".to_owned()))
        }
    }

    pub fn generate_cert(&self, common_name: &str, san: Option<&[&str]>)
    -> Result<(Certificate, PrivateKey), KawsError> {
        let (tempdir, cert_path, key_path) = self.temporary_write()?;

        let mut command = Command::new("cfssl");

        command.args(&[
            "gencert",
            "-ca",
            &cert_path,
            "-ca-key",
            &key_path,
            "-",
        ]);

        if let Some(san) = san {
            command.args(&[
                "-hostname",
                &san.join(","),
            ]);
        }

        command.arg("-");

        command.stdin(Stdio::piped());
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        let mut child = command.spawn()?;

        match child.stdin.as_mut() {
            Some(stdin) => {
                stdin.write_all(
                    format!(
                        r#"{{"CN":"{}","key":{{"algo":"rsa","size":2048}}}}}}"#,
                        common_name
                    ).as_bytes()
                )?;
            }
            None => {
                return Err(
                    KawsError::new("failed to acquire handle to stdin of child process".to_owned())
                );
            }
        }

        let output = child.wait_with_output()?;

        let result = if output.status.success() {
            let raw: CfsslGencertResponse = from_slice(&output.stdout)?;

            Ok((raw.cert.into(), raw.key.into()))
        } else {
            Err(KawsError::new("Execution of `cfssl gencert` failed.".to_owned()))
        };

        tempdir.close()?;

        result
    }

    pub fn sign(&self, csr: &CertificateSigningRequest) -> Result<Certificate, KawsError> {
        let (tempdir, cert_path, key_path) = self.temporary_write()?;

        let mut command = Command::new("cfssl");

        command.args(&[
            "sign",
            "-ca",
            &cert_path,
            "-ca-key",
            &key_path,
            "-"
        ]);

        command.stdin(Stdio::piped());
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        let mut child = command.spawn()?;

        match child.stdin.as_mut() {
            Some(stdin) => {
                stdin.write_all(csr.as_bytes())?;
            }
            None => {
                return Err(
                    KawsError::new("failed to acquire handle to stdin of child process".to_owned())
                );
            }
        }

        let output = child.wait_with_output()?;

        let result = if output.status.success() {
            let response: CfsslSignResponse = from_slice(&output.stdout)?;

            Ok(response.cert.into())
        } else {
            Err(KawsError::new("Execution of `cfssl gencert` failed.".to_owned()))
        };

        tempdir.close()?;

        result
    }

    pub fn write_to_files(
        &self,
        encryptor: &mut Encryptor<ChainProvider, Client>,
        cert_file_path: &str,
        key_file_path: &str,
    ) -> KawsResult {
        let mut cert_file = File::create(cert_file_path)?;
        cert_file.write_all(self.as_bytes())?;

        encryptor.encrypt_and_write_file(self.key.as_bytes(), key_file_path)?;

        Ok(None)
    }

    pub fn as_bytes(&self) -> &[u8] {
        self.cert.as_bytes()
    }

    // Private

    fn temporary_write(&self) -> Result<(TempDir, String, String), KawsError> {
        let tempdir = TempDir::new("kaws")?;

        let cert_path = tempdir.path().join("cert.pem");
        let key_path = tempdir.path().join("key.pem");
        let cert_path_string = match cert_path.to_str() {
            Some(value) => value.to_owned(),
            None => return Err(KawsError::new("Temporary path was invalid UTF-8".to_owned())),
        };
        let key_path_string = match key_path.to_str() {
            Some(value) => value.to_owned(),
            None => return Err(KawsError::new("Temporary path was invalid UTF-8".to_owned())),
        };
        let mut cert_file = File::create(cert_path)?;
        let mut key_file = File::create(key_path)?;
        cert_file.write_all(self.cert.as_bytes())?;
        key_file.write_all(self.key.as_bytes())?;

        Ok((tempdir, cert_path_string, key_path_string))
    }

}

impl From<CfsslGencertResponse> for CertificateAuthority {
    fn from(raw: CfsslGencertResponse) -> Self {
        CertificateAuthority {
            cert: raw.cert.into(),
            key: raw.key.into(),
        }
    }
}

impl CertificateSigningRequest {
    pub fn generate(common_name: &str)
    -> Result<(CertificateSigningRequest, PrivateKey), KawsError> {
        let mut command = Command::new("cfssl");

        command.args(&[
            "genkey",
            "-",
        ]);

        command.stdin(Stdio::piped());
        command.stdout(Stdio::piped());
        command.stderr(Stdio::piped());

        let mut child = command.spawn()?;

        match child.stdin.as_mut() {
            Some(stdin) => {
                stdin.write_all(
                    format!(
                        r#"{{"CN":"{}","key":{{"algo":"rsa","size":2048}}}}}}"#,
                        common_name
                    ).as_bytes(),
                )?;
            }
            None => {
                return Err(
                    KawsError::new("failed to acquire handle to stdin of child process".to_owned())
                );
            }
        };

        let output = child.wait_with_output()?;

        if output.status.success() {
            let raw: CfsslGenkeyResponse = from_slice(&output.stdout)?;

            Ok((CertificateSigningRequest(raw.csr), PrivateKey(raw.key)))
        } else {
            Err(KawsError::new("Execution of `cfssl genkey` failed.".to_owned()))
        }
    }

    pub fn as_bytes(&self) -> &[u8] {
        &self.0
    }
}

impl From<Vec<u8>> for CertificateSigningRequest {
    fn from(vec: Vec<u8>) -> Self {
        CertificateSigningRequest(vec)
    }
}

impl PrivateKey {
    pub fn as_bytes(&self) -> &[u8] {
        &self.0
    }

    pub fn write_to_file(
        &self,
        encryptor: &mut Encryptor<ChainProvider, Client>,
        file_path: &str,
    ) -> KawsResult {
        encryptor.encrypt_and_write_file(self.as_bytes(), file_path)?;

        Ok(None)
    }
}

impl From<Vec<u8>> for PrivateKey {
    fn from(vec: Vec<u8>) -> Self {
        PrivateKey(vec)
    }
}
<|endoftext|>


<commit_before>use std::mem;
use std::ptr;
use std::fs::OpenOptions;
use std::os::unix::fs::OpenOptionsExt;

const BCM2708_PERI_BASE: u32 = 0x20000000;
const GPIO_BASE: u32 = BCM2708_PERI_BASE + 0x200000;
const O_SYNC: u32 = 1052672;

enum Void {} // void type

struct bcm2835_peripheral {
    addr_p: u32,
    mem_fd: i32,
    map: *mut Void,
    addr: *mut i32
}

fn main() {
    unsafe {let gpio = bcm2835_peripheral { addr_p: GPIO_BASE, mem_fd: 0, map: mem::uninitialized(), addr: ptr::null_mut()};}
}

fn map_peripheral(foo: bcm2835_peripheral) {
    let file = OpenOptions::new()
                    .read(true)
                    .write(true)
                    .mode(O_SYNC)
                    .open("/dev/mem");
    //Finish when you have time
}
<commit_msg>add file error handling<commit_after>use std::mem;
use std::ptr;
use std::fs::OpenOptions;
use std::os::unix::fs::OpenOptionsExt;

const BCM2708_PERI_BASE: u32 = 0x20000000;
const GPIO_BASE: u32 = BCM2708_PERI_BASE + 0x200000;
const O_SYNC: u32 = 1052672;

enum Void {} // void type

struct bcm2835_peripheral {
    addr_p: u32,
    mem_fd: i32,
    map: *mut Void,
    addr: *mut i32
}

fn main() {
    unsafe {let gpio = bcm2835_peripheral { addr_p: GPIO_BASE, mem_fd: 0, map: mem::uninitialized(), addr: ptr::null_mut()};}
}

fn map_peripheral(foo: bcm2835_peripheral) {
    let file = OpenOptions::new()
                    .read(true)
                    .write(true)
                    .mode(O_SYNC)
                    .open("/dev/mem");
    match file {
        Ok(file) => println!("Success"),
        Err(e) => println!("Could not open /dev/mem, are you root?")
    }
    //Finish when you have time
}
<|endoftext|>


<commit_before>// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors,
// refer to the Cargo.toml file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Generic rays

use std::marker::PhantomData;

use cgmath::{BaseFloat, BaseNum};
use cgmath::{Point2, Point3};
use cgmath::{Vector2, Vector3};
use cgmath::prelude::*;

use traits::{Continuous, Discrete};

/// A generic ray starting at `origin` and extending infinitely in
/// `direction`.
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "eders", derive(Serialize, Deserialize))]
pub struct Ray<S, P, V> {
    /// Ray origin
    pub origin: P,
    /// Normalized ray direction
    pub direction: V,
    phantom_s: PhantomData<S>,
}

impl<S, V, P> Ray<S, P, V>
where
    S: BaseNum,
    V: VectorSpace<Scalar = S>,
    P: EuclideanSpace<Scalar = S, Diff = V>,
{
    /// Create a generic ray starting at `origin` and extending infinitely in
    /// `direction`.
    pub fn new(origin: P, direction: V) -> Ray<S, P, V> {
        Ray {
            origin: origin,
            direction: direction,
            phantom_s: PhantomData,
        }
    }
}

/// 2D ray
pub type Ray2<S> = Ray<S, Point2<S>, Vector2<S>>;

/// 3D ray
pub type Ray3<S> = Ray<S, Point3<S>, Vector3<S>>;

impl<S, P> Continuous<Ray<S, P, P::Diff>> for P
where
    S: BaseFloat,
    P: EuclideanSpace<Scalar = S>,
    P::Diff: InnerSpace<Scalar = S>,
{
    type Result = P;
    fn intersection(&self, ray: &Ray<S, P, P::Diff>) -> Option<P> {
        if self.intersects(ray) {
            Some(self.clone())
        } else {
            None
        }
    }
}

impl<S, P> Discrete<Ray<S, P, P::Diff>> for P
where
    S: BaseFloat,
    P: EuclideanSpace<Scalar = S>,
    P::Diff: InnerSpace<Scalar = S>,
{
    fn intersects(&self, ray: &Ray<S, P, P::Diff>) -> bool {
        let p = self.clone();
        let l = p - ray.origin;
        let tca = l.dot(ray.direction);
        tca > S::zero() &&
            (tca * tca).relative_eq(
                &l.magnitude2(),
                S::default_epsilon(),
                S::default_max_relative(),
            )
    }
}
<commit_msg>Adding ray transform function. Creates a new ray based on ray + transform<commit_after>// Copyright 2013-2014 The CGMath Developers. For a full listing of the authors,
// refer to the Cargo.toml file at the top-level directory of this distribution.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

//! Generic rays

use std::marker::PhantomData;

use cgmath::{BaseFloat, BaseNum};
use cgmath::{Point2, Point3};
use cgmath::{Vector2, Vector3};
use cgmath::prelude::*;

use traits::{Continuous, Discrete};

/// A generic ray starting at `origin` and extending infinitely in
/// `direction`.
#[derive(Copy, Clone, PartialEq, Debug)]
#[cfg_attr(feature = "eders", derive(Serialize, Deserialize))]
pub struct Ray<S, P, V> {
    /// Ray origin
    pub origin: P,
    /// Normalized ray direction
    pub direction: V,
    phantom_s: PhantomData<S>,
}

impl<S, V, P> Ray<S, P, V>
where
    S: BaseNum,
    V: VectorSpace<Scalar = S>,
    P: EuclideanSpace<Scalar = S, Diff = V>,
{
    /// Create a generic ray starting at `origin` and extending infinitely in
    /// `direction`.
    pub fn new(origin: P, direction: V) -> Ray<S, P, V> {
        Ray {
            origin: origin,
            direction: direction,
            phantom_s: PhantomData,
        }
    }

    /// Create a new ray by applying a transform.
    pub fn transform<T>(&self, transform: T) -> Self
    where
        T: Transform<P>
    {
        Self::new(transform.transform_point(self.origin),
                  transform.transform_vector(self.direction))
    }
}

/// 2D ray
pub type Ray2<S> = Ray<S, Point2<S>, Vector2<S>>;

/// 3D ray
pub type Ray3<S> = Ray<S, Point3<S>, Vector3<S>>;

impl<S, P> Continuous<Ray<S, P, P::Diff>> for P
where
    S: BaseFloat,
    P: EuclideanSpace<Scalar = S>,
    P::Diff: InnerSpace<Scalar = S>,
{
    type Result = P;
    fn intersection(&self, ray: &Ray<S, P, P::Diff>) -> Option<P> {
        if self.intersects(ray) {
            Some(self.clone())
        } else {
            None
        }
    }
}

impl<S, P> Discrete<Ray<S, P, P::Diff>> for P
where
    S: BaseFloat,
    P: EuclideanSpace<Scalar = S>,
    P::Diff: InnerSpace<Scalar = S>,
{
    fn intersects(&self, ray: &Ray<S, P, P::Diff>) -> bool {
        let p = self.clone();
        let l = p - ray.origin;
        let tca = l.dot(ray.direction);
        tca > S::zero() &&
            (tca * tca).relative_eq(
                &l.magnitude2(),
                S::default_epsilon(),
                S::default_max_relative(),
            )
    }
}
<|endoftext|>


<commit_before>use std::sync::mpsc;
use std::thread;
use bencode::{self, BEncode};
use {amy, tiny_http, serde_json, control, CONTROL};

pub struct Handle {
    pub tx: mpsc::Sender<Response>,
}

impl Handle {
    pub fn init(&self) { }

    pub fn get(&self) -> mpsc::Sender<Response> {
        self.tx.clone()
    }
}

unsafe impl Sync for Handle {}

pub struct RPC {
    rx: mpsc::Receiver<Response>,
    tx: amy::Sender<control::Request>,
}

impl RPC {
    pub fn new(rx: mpsc::Receiver<Response>) -> RPC {
        RPC {
            rx,
            tx: CONTROL.ctrl_tx.lock().unwrap().try_clone().unwrap(),
        }
    }

    pub fn run(&mut self) {
        let server = tiny_http::Server::http("0.0.0.0:8412").unwrap();
        for mut request in server.incoming_requests() {
            let mut resp = None;
            if request.url() == "/torrent/list" {
                self.tx.send(control::Request::RPC(Request::ListTorrents)).unwrap();
            } else if request.url().starts_with("/torrent/info/") {
                let res = {
                    let (_, id) = request.url().split_at(14);
                    id.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::TorrentInfo(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad ID!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/torrent/stop/") {
                let res = {
                    let (_, id) = request.url().split_at(14);
                    id.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::StopTorrent(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad ID!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/torrent/start/") {
                let res = {
                    let (_, id) = request.url().split_at(15);
                    id.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::StartTorrent(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad ID!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/torrent/remove/") {
                let res = {
                    let (_, id) = request.url().split_at(16);
                    id.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::RemoveTorrent(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad ID!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/torrent") && request.method() == &tiny_http::Method::Post {
                let mut data = Vec::new();
                request.as_reader().read_to_end(&mut data).unwrap();
                if let Ok(b) = bencode::decode_buf(&mut data) {
                    self.tx.send(control::Request::RPC(Request::AddTorrent(b))).unwrap();
                } else {
                    resp = Some("Invalid torrent file!");

                }
            } else {
                resp = Some("Invalid URL!");
            }

            let r = if let Some(e) = resp {
                tiny_http::Response::from_string(e)
            } else {
                let resp = self.rx.recv().unwrap();
                let s = serde_json::to_string(&resp).unwrap();
                tiny_http::Response::from_string(s)
            };
            request.respond(r).unwrap();
        }
    }
}

pub enum Request {
    ListTorrents,
    TorrentInfo(usize),
    AddTorrent(BEncode),
    StopTorrent(usize),
    StartTorrent(usize),
    RemoveTorrent(usize),
    ThrottleUpload(usize),
    ThrottleDownload(usize),
}

#[derive(Serialize, Debug)]
pub enum Response {
    Torrents(Vec<usize>),
    TorrentInfo(TorrentInfo),
    AddResult(Result<usize, &'static str>),
    Ack,
    Err(&'static str),
}

#[derive(Serialize, Debug)]
pub struct TorrentInfo {
    pub name: String,
    pub status: Status,
    pub size: usize,
    pub downloaded: usize,
    pub uploaded: usize,
    pub tracker: String,
}

#[derive(Serialize, Debug)]
pub enum Status {
    Downloading,
    Seeding,
    Paused,
}

pub fn start() -> Handle {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let mut d = RPC::new(rx);
        d.run();
    });
    Handle { tx }
}
<commit_msg>Add RPC support for throttling<commit_after>use std::sync::mpsc;
use std::thread;
use bencode::{self, BEncode};
use {amy, tiny_http, serde_json, control, CONTROL};

pub struct Handle {
    pub tx: mpsc::Sender<Response>,
}

impl Handle {
    pub fn init(&self) { }

    pub fn get(&self) -> mpsc::Sender<Response> {
        self.tx.clone()
    }
}

unsafe impl Sync for Handle {}

pub struct RPC {
    rx: mpsc::Receiver<Response>,
    tx: amy::Sender<control::Request>,
}

impl RPC {
    pub fn new(rx: mpsc::Receiver<Response>) -> RPC {
        RPC {
            rx,
            tx: CONTROL.ctrl_tx.lock().unwrap().try_clone().unwrap(),
        }
    }

    pub fn run(&mut self) {
        let server = tiny_http::Server::http("0.0.0.0:8412").unwrap();
        for mut request in server.incoming_requests() {
            let mut resp = None;
            if request.url() == "/torrent/list" {
                self.tx.send(control::Request::RPC(Request::ListTorrents)).unwrap();
            } else if request.url().starts_with("/torrent/info/") {
                let res = {
                    let (_, id) = request.url().split_at(14);
                    id.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::TorrentInfo(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad ID!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/torrent/stop/") {
                let res = {
                    let (_, id) = request.url().split_at(14);
                    id.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::StopTorrent(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad ID!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/torrent/start/") {
                let res = {
                    let (_, id) = request.url().split_at(15);
                    id.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::StartTorrent(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad ID!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/torrent/remove/") {
                let res = {
                    let (_, id) = request.url().split_at(16);
                    id.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::RemoveTorrent(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad ID!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/torrent") && request.method() == &tiny_http::Method::Post {
                let mut data = Vec::new();
                request.as_reader().read_to_end(&mut data).unwrap();
                if let Ok(b) = bencode::decode_buf(&mut data) {
                    self.tx.send(control::Request::RPC(Request::AddTorrent(b))).unwrap();
                } else {
                    resp = Some("Invalid torrent file!");

                }
            } else if request.url().starts_with("/rate/upload/") {
                let res = {
                    let (_, amnt) = request.url().split_at(13);
                    amnt.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::ThrottleUpload(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad amount!"); }
                    _ => { }
                };
            } else if request.url().starts_with("/rate/download/") {
                let res = {
                    let (_, amnt) = request.url().split_at(15);
                    amnt.parse::<usize>().map(|i| {
                        self.tx.send(control::Request::RPC(Request::ThrottleDownload(i))).unwrap();
                    })
                };
                match res {
                    Err(_) => { resp = Some("Bad amount!"); }
                    _ => { }
                };
            } else {
                resp = Some("Invalid URL!");
            }

            let r = if let Some(e) = resp {
                tiny_http::Response::from_string(e)
            } else {
                let resp = self.rx.recv().unwrap();
                let s = serde_json::to_string(&resp).unwrap();
                tiny_http::Response::from_string(s)
            };
            request.respond(r).unwrap();
        }
    }
}

pub enum Request {
    ListTorrents,
    TorrentInfo(usize),
    AddTorrent(BEncode),
    StopTorrent(usize),
    StartTorrent(usize),
    RemoveTorrent(usize),
    ThrottleUpload(usize),
    ThrottleDownload(usize),
}

#[derive(Serialize, Debug)]
pub enum Response {
    Torrents(Vec<usize>),
    TorrentInfo(TorrentInfo),
    AddResult(Result<usize, &'static str>),
    Ack,
    Err(&'static str),
}

#[derive(Serialize, Debug)]
pub struct TorrentInfo {
    pub name: String,
    pub status: Status,
    pub size: usize,
    pub downloaded: usize,
    pub uploaded: usize,
    pub tracker: String,
}

#[derive(Serialize, Debug)]
pub enum Status {
    Downloading,
    Seeding,
    Paused,
}

pub fn start() -> Handle {
    let (tx, rx) = mpsc::channel();
    thread::spawn(move || {
        let mut d = RPC::new(rx);
        d.run();
    });
    Handle { tx }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::sync::Arc;
use std::sync::mpsc::Receiver;
use std::collections::HashMap;
use xcb::{ self, Atom };
use ::{ INCR_CHUNK_SIZE, Context, SetMap };

macro_rules! try_continue {
    ( $expr:expr ) => {
        match $expr {
            Some(val) => val,
            None => continue
        }
    };
}

struct IncrState {
    selection: Atom,
    requestor: Atom,
    property: Atom,
    pos: usize
}

#[inline]
pub fn run(context: Arc<Context>, setmap: SetMap, max_length: usize, receiver: &Receiver<Atom>) {
    let mut incr_map = HashMap::new();
    let mut state_map = HashMap::new();

    while let Some(event) = context.connection.wait_for_event() {
        if let Some(property) = receiver
            .try_recv().ok()
            .and_then(|selection| incr_map.remove(&selection))
        {
            state_map.remove(&property);
        }

        match event.response_type() & !0x80 {
            xcb::SELECTION_REQUEST => {
                let event = unsafe { xcb::cast_event::<xcb::SelectionRequestEvent>(&event) };
                let read_map = try_continue!(setmap.read().ok());
                let &(target, ref value) = try_continue!(read_map.get(&event.selection()));

                if event.target() == context.atoms.targets {
                    xcb::change_property(
                        &context.connection, xcb::PROP_MODE_REPLACE as u8,
                        event.requestor(), event.property(), xcb::ATOM_ATOM, 32,
                        &[context.atoms.targets, target]
                    );
                } else if event.target() == target {
                    if value.len() < max_length - 24 {
                        xcb::change_property(
                            &context.connection, xcb::PROP_MODE_REPLACE as u8,
                            event.requestor(), event.property(), target, 8,
                            value
                        );
                    } else {
                        xcb::change_window_attributes(
                            &context.connection, event.requestor(),
                            &[(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_PROPERTY_CHANGE)]
                        );
                        xcb::change_property(
                            &context.connection, xcb::PROP_MODE_REPLACE as u8,
                            event.requestor(), event.property(), context.atoms.incr, 32,
                            &[0u8; 0]
                        );

                        incr_map.insert(event.selection(), event.property());
                        state_map.insert(
                            event.property(),
                            IncrState {
                                selection: event.selection(),
                                requestor: event.requestor(),
                                property: event.property(),
                                pos: 0
                            }
                        );
                    }
                } else {
                    continue
                }

                xcb::send_event(
                    &context.connection, false, event.requestor(), 0,
                    &xcb::SelectionNotifyEvent::new(
                        event.time(),
                        event.requestor(),
                        event.selection(),
                        event.target(),
                        event.property()
                    )
                );
                context.connection.flush();
            },
            xcb::PROPERTY_NOTIFY => {
                let event = unsafe { xcb::cast_event::<xcb::PropertyNotifyEvent>(&event) };
                if event.state() != xcb::PROPERTY_DELETE as u8 { continue };

                let is_end = {
                    let state = try_continue!(state_map.get_mut(&event.atom()));
                    let read_setmap = try_continue!(setmap.read().ok());
                    let &(target, ref value) = try_continue!(read_setmap.get(&state.selection));

                    let len = cmp::min(INCR_CHUNK_SIZE, value.len() - state.pos);
                    xcb::change_property(
                        &context.connection, xcb::PROP_MODE_REPLACE as u8,
                        state.requestor, state.property, target, 8,
                        &value[state.pos..(state.pos + len)]
                    );

                    state.pos += len;
                    len == 0
                };

                if is_end {
                    state_map.remove(&event.atom());
                }
                context.connection.flush();
            },
            xcb::SELECTION_CLEAR => {
                let event = unsafe { xcb::cast_event::<xcb::SelectionClearEvent>(&event) };
                if let Some(property) = incr_map.remove(&event.selection()) {
                    state_map.remove(&property);
                }
                if let Ok(mut write_setmap) = setmap.write() {
                    write_setmap.remove(&event.selection());
                }
            },
            _ => ()
        }
    }
}
<commit_msg>[Improved] handle all recv<commit_after>use std::cmp;
use std::sync::Arc;
use std::sync::mpsc::Receiver;
use std::collections::HashMap;
use xcb::{ self, Atom };
use ::{ INCR_CHUNK_SIZE, Context, SetMap };

macro_rules! try_continue {
    ( $expr:expr ) => {
        match $expr {
            Some(val) => val,
            None => continue
        }
    };
}

struct IncrState {
    selection: Atom,
    requestor: Atom,
    property: Atom,
    pos: usize
}

#[inline]
pub fn run(context: Arc<Context>, setmap: SetMap, max_length: usize, receiver: &Receiver<Atom>) {
    let mut incr_map = HashMap::new();
    let mut state_map = HashMap::new();

    while let Some(event) = context.connection.wait_for_event() {
        while let Ok(selection) = receiver.try_recv() {
            if let Some(property) = incr_map.remove(&selection) {
                state_map.remove(&property);
            }
        }

        match event.response_type() & !0x80 {
            xcb::SELECTION_REQUEST => {
                let event = unsafe { xcb::cast_event::<xcb::SelectionRequestEvent>(&event) };
                let read_map = try_continue!(setmap.read().ok());
                let &(target, ref value) = try_continue!(read_map.get(&event.selection()));

                if event.target() == context.atoms.targets {
                    xcb::change_property(
                        &context.connection, xcb::PROP_MODE_REPLACE as u8,
                        event.requestor(), event.property(), xcb::ATOM_ATOM, 32,
                        &[context.atoms.targets, target]
                    );
                } else if event.target() == target {
                    if value.len() < max_length - 24 {
                        xcb::change_property(
                            &context.connection, xcb::PROP_MODE_REPLACE as u8,
                            event.requestor(), event.property(), target, 8,
                            value
                        );
                    } else {
                        xcb::change_window_attributes(
                            &context.connection, event.requestor(),
                            &[(xcb::CW_EVENT_MASK, xcb::EVENT_MASK_PROPERTY_CHANGE)]
                        );
                        xcb::change_property(
                            &context.connection, xcb::PROP_MODE_REPLACE as u8,
                            event.requestor(), event.property(), context.atoms.incr, 32,
                            &[0u8; 0]
                        );

                        incr_map.insert(event.selection(), event.property());
                        state_map.insert(
                            event.property(),
                            IncrState {
                                selection: event.selection(),
                                requestor: event.requestor(),
                                property: event.property(),
                                pos: 0
                            }
                        );
                    }
                } else {
                    continue
                }

                xcb::send_event(
                    &context.connection, false, event.requestor(), 0,
                    &xcb::SelectionNotifyEvent::new(
                        event.time(),
                        event.requestor(),
                        event.selection(),
                        event.target(),
                        event.property()
                    )
                );
                context.connection.flush();
            },
            xcb::PROPERTY_NOTIFY => {
                let event = unsafe { xcb::cast_event::<xcb::PropertyNotifyEvent>(&event) };
                if event.state() != xcb::PROPERTY_DELETE as u8 { continue };

                let is_end = {
                    let state = try_continue!(state_map.get_mut(&event.atom()));
                    let read_setmap = try_continue!(setmap.read().ok());
                    let &(target, ref value) = try_continue!(read_setmap.get(&state.selection));

                    let len = cmp::min(INCR_CHUNK_SIZE, value.len() - state.pos);
                    xcb::change_property(
                        &context.connection, xcb::PROP_MODE_REPLACE as u8,
                        state.requestor, state.property, target, 8,
                        &value[state.pos..(state.pos + len)]
                    );

                    state.pos += len;
                    len == 0
                };

                if is_end {
                    state_map.remove(&event.atom());
                }
                context.connection.flush();
            },
            xcb::SELECTION_CLEAR => {
                let event = unsafe { xcb::cast_event::<xcb::SelectionClearEvent>(&event) };
                if let Some(property) = incr_map.remove(&event.selection()) {
                    state_map.remove(&property);
                }
                if let Ok(mut write_setmap) = setmap.write() {
                    write_setmap.remove(&event.selection());
                }
            },
            _ => ()
        }
    }
}
<|endoftext|>