<commit_before>use std::{mem, ptr, int};
use std::num::Int;
use alloc::heap;
use os::token::Token;

/// A preallocated chunk of memory for storing objects of the same type.
pub struct Slab<T> {
    // Chunk of memory
    mem: *mut Entry<T>,
    // Number of elements currently in the slab
    len: int,
    // The total number of elements that the slab can hold
    cap: int,
    // THe token offset
    off: uint,
    // Offset of the next available slot in the slab. Set to the slab's
    // capacity when the slab is full.
    nxt: int,
    // The total number of slots that were initialized
    init: int,
}

const MAX: uint = int::MAX as uint;

// When Entry.nxt is set to this, the entry is in use
const IN_USE: int = -1;

impl<T> Slab<T> {
    pub fn new(cap: uint) -> Slab<T> {
        Slab::new_starting_at(Token(0), cap)
    }

    pub fn new_starting_at(offset: Token, cap: uint) -> Slab<T> {
        assert!(cap <= MAX, "capacity too large");
        // TODO:
        // - Rename to with_capacity
        // - Use a power of 2 capacity
        // - Ensure that mem size is less than uint::MAX

        let size = cap.checked_mul(mem::size_of::<Entry<T>>())
            .expect("capacity overflow");

        let ptr = unsafe { heap::allocate(size, mem::min_align_of::<Entry<T>>()) };

        Slab {
            mem: ptr as *mut Entry<T>,
            cap: cap as int,
            len: 0,
            off: offset.as_uint(),
            nxt: 0,
            init: 0,
        }
    }

    #[inline]
    pub fn count(&self) -> uint {
        self.len as uint
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    #[inline]
    pub fn remaining(&self) -> uint {
        (self.cap - self.len) as uint
    }

    #[inline]
    pub fn has_remaining(&self) -> bool {
        self.remaining() > 0
    }

    #[inline]
    pub fn contains(&self, idx: Token) -> bool {
        let idx = self.token_to_idx(idx);

        if idx <= MAX {
            let idx = idx as int;

            if idx < self.init {
                return self.entry(idx).in_use();
            }
        }

        false
    }

    pub fn get(&self, idx: Token) -> Option<&T> {
        let idx = self.token_to_idx(idx);

        if idx <= MAX {
            let idx = idx as int;

            if idx < self.init {
                let entry = self.entry(idx);

                if entry.in_use() {
                    return Some(&entry.val);
                }
            }
        }

        None
    }

    pub fn insert(&mut self, val: T) -> Result<Token, T> {
        let idx = self.nxt;

        if idx == self.init {
            // Using an uninitialized entry
            if idx == self.cap {
                // No more capacity
                debug!("slab out of capacity; cap={}", self.cap);
                return Err(val);
            }

            self.mut_entry(idx).put(val, true);

            self.init += 1;
            self.len = self.init;
            self.nxt = self.init;

            debug!("inserting into new slot; idx={}", idx);
        }
        else {
            self.len += 1;
            self.nxt = self.mut_entry(idx).put(val, false);

            debug!("inserting into reused slot; idx={}", idx);
        }

        Ok(self.idx_to_token(idx))
    }

    /// Releases the given slot
    pub fn remove(&mut self, idx: Token) -> Option<T> {
        debug!("removing value; idx={}", idx);

        // Cast to uint
        let idx = self.token_to_idx(idx);

        if idx > MAX {
            return None;
        }

        let idx = idx as int;

        // Ensure index is within capacity of slab
        if idx >= self.init {
            return None;
        }

        let nxt = self.nxt;

        match self.mut_entry(idx).remove(nxt) {
            Some(v) => {
                self.nxt = idx;
                self.len -= 1;
                Some(v)
            }
            None => None
        }
    }

    #[inline]
    fn entry(&self, idx: int) -> &Entry<T> {
        unsafe { &*self.mem.offset(idx) }
    }

    #[inline]
    fn mut_entry(&mut self, idx: int) -> &mut Entry<T> {
        unsafe { &mut *self.mem.offset(idx) }
    }

    #[inline]
    fn validate_idx(&self, idx: uint) -> int {
        if idx <= MAX {
            let idx = idx as int;

            if idx < self.init {
                return idx;
            }
        }

        panic!("invalid index {} -- greater than capacity {}", idx, self.cap);
    }

    fn token_to_idx(&self, token: Token) -> uint {
        token.as_uint() - self.off
    }

    fn idx_to_token(&self, idx: int) -> Token {
        Token(idx as uint + self.off)
    }
}

impl<T> Index<Token, T> for Slab<T> {
    fn index<'a>(&'a self, idx: &Token) -> &'a T {
        let idx = self.token_to_idx(*idx);
        let idx = self.validate_idx(idx);

        let e = self.entry(idx);

        if !e.in_use() {
            panic!("invalid index; idx={}", idx);
        }

        &e.val
    }
}

impl<T> IndexMut<Token, T> for Slab<T> {
    fn index_mut<'a>(&'a mut self, idx: &Token) -> &'a mut T {
        let idx = self.token_to_idx(*idx);
        let idx = self.validate_idx(idx);

        let e = self.mut_entry(idx);

        if !e.in_use() {
            panic!("invalid index; idx={}", idx);
        }

        &mut e.val
    }
}

#[unsafe_destructor]
impl<T> Drop for Slab<T> {
    fn drop(&mut self) {
        // TODO: check whether or not this is needed with intrinsics::needs_drop
        let mut i = 0;

        while i < self.init {
            self.mut_entry(i).release();
            i += 1;
        }

        let cap = self.cap as uint;
        let size = cap.checked_mul(mem::size_of::<Entry<T>>()).unwrap();
        unsafe { heap::deallocate(self.mem as *mut u8, size, mem::min_align_of::<Entry<T>>()) };
    }
}

// Holds the values in the slab.
struct Entry<T> {
    nxt: int,
    val: T
}

impl<T> Entry<T> {
    #[inline]
    fn put(&mut self, val: T, init: bool) -> int {
        assert!(init || self.nxt != IN_USE);

        let ret = self.nxt;

        unsafe { ptr::write(&mut self.val as *mut T, val); }
        self.nxt = IN_USE;

        // Could be uninitialized memory, but the caller (Slab) should guard
        // not use the return value in those cases.
        ret
    }

    fn remove(&mut self, nxt: int) -> Option<T> {
        if self.in_use() {
            self.nxt = nxt;
            Some(unsafe { ptr::read(&self.val as *const T) })
        } else {
            None
        }
    }

    fn release(&mut self) {
        if self.in_use() {
            let _ = Some(unsafe { ptr::read(&self.val as *const T) });
        }
    }

    #[inline]
    fn in_use(&self) -> bool {
        self.nxt == IN_USE
    }
}

#[cfg(test)]
mod tests {
    use super::Slab;
    use {Token};

    #[test]
    fn test_insertion() {
        let mut slab = Slab::new(1);
        let token = slab.insert(10u).ok().expect("Failed to insert");
        assert_eq!(slab[token], 10u);
    }

    #[test]
    fn test_repeated_insertion() {
        let mut slab = Slab::new(10);

        for i in range(0u, 10u) {
            let token = slab.insert(i + 10u).ok().expect("Failed to insert");
            assert_eq!(slab[token], i + 10u);
        }

        slab.insert(20).err().expect("Inserted when full");
    }

    #[test]
    fn test_repeated_insertion_and_removal() {
        let mut slab = Slab::new(10);
        let mut tokens = vec![];

        for i in range(0u, 10u) {
            let token = slab.insert(i + 10u).ok().expect("Failed to insert");
            tokens.push(token);
            assert_eq!(slab[token], i + 10u);
        }

        for &i in tokens.iter() {
            slab.remove(i);
        }

        slab.insert(20).ok().expect("Failed to insert in newly empty slab");
    }

    #[test]
    fn test_insertion_when_full() {
        let mut slab = Slab::new(1);
        slab.insert(10u).ok().expect("Failed to insert");
        slab.insert(10u).err().expect("Inserted into a full slab");
    }

    #[test]
    fn test_removal_is_successful() {
        let mut slab = Slab::new(1);
        let t1 = slab.insert(10u).ok().expect("Failed to insert");
        slab.remove(t1);
        let t2 = slab.insert(20u).ok().expect("Failed to insert");
        assert_eq!(slab[t2], 20u);
    }

    #[test]
    fn test_mut_retrieval() {
        let mut slab = Slab::new(1);
        let t1 = slab.insert("foo".to_string()).ok().expect("Failed to insert");

        slab[t1].push_str("bar");

        assert_eq!(slab[t1].as_slice(), "foobar");
    }

    #[test]
    #[should_fail]
    fn test_reusing_slots_1() {
        let mut slab = Slab::new(16);

        let t0 = slab.insert(123u).unwrap();
        let t1 = slab.insert(456u).unwrap();

        assert!(slab.count() == 2);
        assert!(slab.remaining() == 14);

        slab.remove(t0);

        assert!(slab.count() == 1, "actual={}", slab.count());
        assert!(slab.remaining() == 15);

        slab.remove(t1);

        assert!(slab.count() == 0);
        assert!(slab.remaining() == 16);

        let _ = slab[t1];
    }

    #[test]
    fn test_reusing_slots_2() {
        let mut slab = Slab::new(16);

        let t0 = slab.insert(123u).unwrap();

        assert!(slab[t0] == 123u);
        assert!(slab.remove(t0) == Some(123u));

        let t0 = slab.insert(456u).unwrap();

        assert!(slab[t0] == 456u);

        let t1 = slab.insert(789u).unwrap();

        assert!(slab[t0] == 456u);
        assert!(slab[t1] == 789u);

        assert!(slab.remove(t0).unwrap() == 456u);
        assert!(slab.remove(t1).unwrap() == 789u);

        assert!(slab.count() == 0);
    }

    #[test]
    #[should_fail]
    fn test_accessing_out_of_bounds() {
        let slab = Slab::<uint>::new(16);
        slab[Token(0)];
    }

    #[test]
    fn test_contains() {
        let mut slab = Slab::new_starting_at(Token(5),16);
        assert!(!slab.contains(Token(0)));

        let tok = slab.insert(111u).unwrap();
        assert!(slab.contains(tok));
    }
}
<commit_msg>Borrow mutable references with Slab#get_mut<commit_after>use std::{mem, ptr, int};
use std::num::Int;
use alloc::heap;
use os::token::Token;

/// A preallocated chunk of memory for storing objects of the same type.
pub struct Slab<T> {
    // Chunk of memory
    mem: *mut Entry<T>,
    // Number of elements currently in the slab
    len: int,
    // The total number of elements that the slab can hold
    cap: int,
    // THe token offset
    off: uint,
    // Offset of the next available slot in the slab. Set to the slab's
    // capacity when the slab is full.
    nxt: int,
    // The total number of slots that were initialized
    init: int,
}

const MAX: uint = int::MAX as uint;

// When Entry.nxt is set to this, the entry is in use
const IN_USE: int = -1;

impl<T> Slab<T> {
    pub fn new(cap: uint) -> Slab<T> {
        Slab::new_starting_at(Token(0), cap)
    }

    pub fn new_starting_at(offset: Token, cap: uint) -> Slab<T> {
        assert!(cap <= MAX, "capacity too large");
        // TODO:
        // - Rename to with_capacity
        // - Use a power of 2 capacity
        // - Ensure that mem size is less than uint::MAX

        let size = cap.checked_mul(mem::size_of::<Entry<T>>())
            .expect("capacity overflow");

        let ptr = unsafe { heap::allocate(size, mem::min_align_of::<Entry<T>>()) };

        Slab {
            mem: ptr as *mut Entry<T>,
            cap: cap as int,
            len: 0,
            off: offset.as_uint(),
            nxt: 0,
            init: 0,
        }
    }

    #[inline]
    pub fn count(&self) -> uint {
        self.len as uint
    }

    #[inline]
    pub fn is_empty(&self) -> bool {
        self.len == 0
    }

    #[inline]
    pub fn remaining(&self) -> uint {
        (self.cap - self.len) as uint
    }

    #[inline]
    pub fn has_remaining(&self) -> bool {
        self.remaining() > 0
    }

    #[inline]
    pub fn contains(&self, idx: Token) -> bool {
        let idx = self.token_to_idx(idx);

        if idx <= MAX {
            let idx = idx as int;

            if idx < self.init {
                return self.entry(idx).in_use();
            }
        }

        false
    }

    pub fn get(&self, idx: Token) -> Option<&T> {
        let idx = self.token_to_idx(idx);

        if idx <= MAX {
            let idx = idx as int;

            if idx < self.init {
                let entry = self.entry(idx);

                if entry.in_use() {
                    return Some(&entry.val);
                }
            }
        }

        None
    }

    pub fn get_mut(&mut self, idx: Token) -> Option<&mut T> {
        let idx = self.token_to_idx(idx);

        if idx <= MAX {
            let idx = idx as int;

            if idx < self.init {
                let mut entry = self.mut_entry(idx);

                if entry.in_use() {
                    return Some(&mut entry.val);
                }
            }
        }

        None
    }

    pub fn insert(&mut self, val: T) -> Result<Token, T> {
        let idx = self.nxt;

        if idx == self.init {
            // Using an uninitialized entry
            if idx == self.cap {
                // No more capacity
                debug!("slab out of capacity; cap={}", self.cap);
                return Err(val);
            }

            self.mut_entry(idx).put(val, true);

            self.init += 1;
            self.len = self.init;
            self.nxt = self.init;

            debug!("inserting into new slot; idx={}", idx);
        }
        else {
            self.len += 1;
            self.nxt = self.mut_entry(idx).put(val, false);

            debug!("inserting into reused slot; idx={}", idx);
        }

        Ok(self.idx_to_token(idx))
    }

    /// Releases the given slot
    pub fn remove(&mut self, idx: Token) -> Option<T> {
        debug!("removing value; idx={}", idx);

        // Cast to uint
        let idx = self.token_to_idx(idx);

        if idx > MAX {
            return None;
        }

        let idx = idx as int;

        // Ensure index is within capacity of slab
        if idx >= self.init {
            return None;
        }

        let nxt = self.nxt;

        match self.mut_entry(idx).remove(nxt) {
            Some(v) => {
                self.nxt = idx;
                self.len -= 1;
                Some(v)
            }
            None => None
        }
    }

    #[inline]
    fn entry(&self, idx: int) -> &Entry<T> {
        unsafe { &*self.mem.offset(idx) }
    }

    #[inline]
    fn mut_entry(&mut self, idx: int) -> &mut Entry<T> {
        unsafe { &mut *self.mem.offset(idx) }
    }

    #[inline]
    fn validate_idx(&self, idx: uint) -> int {
        if idx <= MAX {
            let idx = idx as int;

            if idx < self.init {
                return idx;
            }
        }

        panic!("invalid index {} -- greater than capacity {}", idx, self.cap);
    }

    fn token_to_idx(&self, token: Token) -> uint {
        token.as_uint() - self.off
    }

    fn idx_to_token(&self, idx: int) -> Token {
        Token(idx as uint + self.off)
    }
}

impl<T> Index<Token, T> for Slab<T> {
    fn index<'a>(&'a self, idx: &Token) -> &'a T {
        let idx = self.token_to_idx(*idx);
        let idx = self.validate_idx(idx);

        let e = self.entry(idx);

        if !e.in_use() {
            panic!("invalid index; idx={}", idx);
        }

        &e.val
    }
}

impl<T> IndexMut<Token, T> for Slab<T> {
    fn index_mut<'a>(&'a mut self, idx: &Token) -> &'a mut T {
        let idx = self.token_to_idx(*idx);
        let idx = self.validate_idx(idx);

        let e = self.mut_entry(idx);

        if !e.in_use() {
            panic!("invalid index; idx={}", idx);
        }

        &mut e.val
    }
}

#[unsafe_destructor]
impl<T> Drop for Slab<T> {
    fn drop(&mut self) {
        // TODO: check whether or not this is needed with intrinsics::needs_drop
        let mut i = 0;

        while i < self.init {
            self.mut_entry(i).release();
            i += 1;
        }

        let cap = self.cap as uint;
        let size = cap.checked_mul(mem::size_of::<Entry<T>>()).unwrap();
        unsafe { heap::deallocate(self.mem as *mut u8, size, mem::min_align_of::<Entry<T>>()) };
    }
}

// Holds the values in the slab.
struct Entry<T> {
    nxt: int,
    val: T
}

impl<T> Entry<T> {
    #[inline]
    fn put(&mut self, val: T, init: bool) -> int {
        assert!(init || self.nxt != IN_USE);

        let ret = self.nxt;

        unsafe { ptr::write(&mut self.val as *mut T, val); }
        self.nxt = IN_USE;

        // Could be uninitialized memory, but the caller (Slab) should guard
        // not use the return value in those cases.
        ret
    }

    fn remove(&mut self, nxt: int) -> Option<T> {
        if self.in_use() {
            self.nxt = nxt;
            Some(unsafe { ptr::read(&self.val as *const T) })
        } else {
            None
        }
    }

    fn release(&mut self) {
        if self.in_use() {
            let _ = Some(unsafe { ptr::read(&self.val as *const T) });
        }
    }

    #[inline]
    fn in_use(&self) -> bool {
        self.nxt == IN_USE
    }
}

#[cfg(test)]
mod tests {
    use super::Slab;
    use {Token};

    #[test]
    fn test_insertion() {
        let mut slab = Slab::new(1);
        let token = slab.insert(10u).ok().expect("Failed to insert");
        assert_eq!(slab[token], 10u);
    }

    #[test]
    fn test_repeated_insertion() {
        let mut slab = Slab::new(10);

        for i in range(0u, 10u) {
            let token = slab.insert(i + 10u).ok().expect("Failed to insert");
            assert_eq!(slab[token], i + 10u);
        }

        slab.insert(20).err().expect("Inserted when full");
    }

    #[test]
    fn test_repeated_insertion_and_removal() {
        let mut slab = Slab::new(10);
        let mut tokens = vec![];

        for i in range(0u, 10u) {
            let token = slab.insert(i + 10u).ok().expect("Failed to insert");
            tokens.push(token);
            assert_eq!(slab[token], i + 10u);
        }

        for &i in tokens.iter() {
            slab.remove(i);
        }

        slab.insert(20).ok().expect("Failed to insert in newly empty slab");
    }

    #[test]
    fn test_insertion_when_full() {
        let mut slab = Slab::new(1);
        slab.insert(10u).ok().expect("Failed to insert");
        slab.insert(10u).err().expect("Inserted into a full slab");
    }

    #[test]
    fn test_removal_is_successful() {
        let mut slab = Slab::new(1);
        let t1 = slab.insert(10u).ok().expect("Failed to insert");
        slab.remove(t1);
        let t2 = slab.insert(20u).ok().expect("Failed to insert");
        assert_eq!(slab[t2], 20u);
    }

    #[test]
    fn test_mut_retrieval() {
        let mut slab = Slab::new(1);
        let t1 = slab.insert("foo".to_string()).ok().expect("Failed to insert");

        slab[t1].push_str("bar");

        assert_eq!(slab[t1].as_slice(), "foobar");
    }

    #[test]
    #[should_fail]
    fn test_reusing_slots_1() {
        let mut slab = Slab::new(16);

        let t0 = slab.insert(123u).unwrap();
        let t1 = slab.insert(456u).unwrap();

        assert!(slab.count() == 2);
        assert!(slab.remaining() == 14);

        slab.remove(t0);

        assert!(slab.count() == 1, "actual={}", slab.count());
        assert!(slab.remaining() == 15);

        slab.remove(t1);

        assert!(slab.count() == 0);
        assert!(slab.remaining() == 16);

        let _ = slab[t1];
    }

    #[test]
    fn test_reusing_slots_2() {
        let mut slab = Slab::new(16);

        let t0 = slab.insert(123u).unwrap();

        assert!(slab[t0] == 123u);
        assert!(slab.remove(t0) == Some(123u));

        let t0 = slab.insert(456u).unwrap();

        assert!(slab[t0] == 456u);

        let t1 = slab.insert(789u).unwrap();

        assert!(slab[t0] == 456u);
        assert!(slab[t1] == 789u);

        assert!(slab.remove(t0).unwrap() == 456u);
        assert!(slab.remove(t1).unwrap() == 789u);

        assert!(slab.count() == 0);
    }

    #[test]
    #[should_fail]
    fn test_accessing_out_of_bounds() {
        let slab = Slab::<uint>::new(16);
        slab[Token(0)];
    }

    #[test]
    fn test_contains() {
        let mut slab = Slab::new_starting_at(Token(5),16);
        assert!(!slab.contains(Token(0)));

        let tok = slab.insert(111u).unwrap();
        assert!(slab.contains(tok));
    }
}
<|endoftext|>


<commit_before>// This file is released under the same terms as Rust itself.

use hex::{FromHex, FromHexError, ToHex};
use hyper;
use hyper::client::Client;
use hyper::header::{Accept, ContentType, Cookie, CookiePair, Headers, Location, SetCookie, UserAgent, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use serde_json::{self, from_reader as json_from_reader};
use std::convert::AsRef;
use url::form_urlencoded;
use util::USER_AGENT;
use util::crypto::{generate_sha1_hmac, verify_sha1_hmac};

pub enum Auth {
    None,
    Github(String, String, String),
}

#[derive(Clone, Copy)]
pub enum AuthRef<'a> {
    None,
    Github(&'a str, &'a str, &'a str),
}

impl<'a> From<&'a Auth> for AuthRef<'a> {
    fn from(a: &'a Auth) -> AuthRef<'a> {
        match *a {
            Auth::None => AuthRef::None,
            Auth::Github(ref app_id, ref app_secret, ref org) =>
                AuthRef::Github(app_id.as_ref(), app_secret.as_ref(), org.as_ref()),
        }
    }
}

pub struct AuthManager<'a> {
    pub auth: AuthRef<'a>,
    pub secret: &'a str,
}

pub enum CheckResult<'a, 'b: 'a, 'c> {
    Authenticated(Request<'a, 'b>, Response<'c>),
    NotAuthenticated,
    Err(CheckResultError),
}

quick_error!{
    #[derive(Debug)]
    pub enum CheckResultError {
        ErrGithubToken(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubTokenResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubUsername(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubUsernameResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubOrgCheck(e: hyper::error::Error) {
            cause(e)
        }
        InvalidGithubStateParam(e: FromHexError) {
            cause(e)
        }
        BadGithubStateParam {}
        MissingGithubParam {}
    }
}

impl<'a> AuthManager<'a> {
    pub fn check<'b, 'c, 'd>(
        &self,
        req: Request<'b, 'c>,
        mut res: Response<'d>,
    ) -> CheckResult<'b, 'c, 'd> {
        macro_rules! try_map {
            ($e: expr, $i: ident, $res: ident) => (
                match $e {
                    Ok(x) => x,
                    Err(e) => {
                        *$res.status_mut() = StatusCode::InternalServerError;
                        return CheckResult::Err(CheckResultError::$i(e));
                    }
                }
            )
        }
        match self.auth {
            AuthRef::None => {
                CheckResult::Authenticated(req, res)
            }
            AuthRef::Github(app_id, app_secret, org) => {
                let authed = req.headers.get::<Cookie>()
                    .and_then(|x| {
                        let mut auth = None;
                        let mut name = None;
                        for cookie in x.iter() {
                            match &cookie.name[..] {
                                "auth" => auth = Some(cookie.value.clone()),
                                "name" => name = Some(cookie.value.clone()),
                                n => {
                                    warn!("Unrecognized cookie {}", n);
                                }
                            }
                        }
                        if let (Some(auth), Some(name)) = (auth, name) {
                            Some((auth, name))
                        } else {
                            None
                        }
                    })
                    .map(|(auth, name)| {
                        match Vec::from_hex(&auth) {
                            Ok(auth) => {
                                verify_sha1_hmac(
                                    self.secret.as_bytes(),
                                    name.as_bytes(),
                                    &auth
                                )
                            }
                            Err(e) => {
                                warn!("Bad auth \"{}\": {:?}", auth, e);
                                false
                            }
                        }
                    })
                    .unwrap_or(false);
                if !authed {
                    let path = if let RequestUri::AbsolutePath(ref path) = req.uri {
                        path
                    } else {
                        *res.status_mut() = StatusCode::BadRequest;
                        return CheckResult::NotAuthenticated;
                    };
                    let path = path.as_bytes();
                    if path.len() > 11 && &path[..11] == b"/_gh_login?" {
                        let query = &path[11..];
                        let query = form_urlencoded::parse(query);
                        let mut code = None;
                        let mut state = None;
                        for param in query {
                            match param.0.as_ref() {
                                "state" => state = Some(param.1.into_owned()),
                                "code" => code = Some(param.1.into_owned()),
                                _ => {
                                    warn!(
                                        "Unrecognized Github oAuth param"
                                    );
                                }
                            }
                        }
                        let (code, state) = if let (Some(code), Some(state)) = (code, state) {
                            (code, state)
                        } else {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::MissingGithubParam);
                        };
                        if !verify_sha1_hmac(
                            self.secret.as_bytes(),
                            &req.remote_addr.ip().to_string().as_bytes(),
                            &try_map!(
                                Vec::from_hex(&state),
                                InvalidGithubStateParam,
                                res
                            )
                        ) {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::BadGithubStateParam);
                        }
                        let client = Client::default();
                        // We got successfully authed to GitHub
                        // But we need to check if we're in the org
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(ContentType::form_url_encoded());
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![])),
                        ]));
                        let gh_req = form_urlencoded::Serializer::new(String::new())
                            .append_pair("client_id", app_id)
                            .append_pair("client_secret", app_secret)
                            .append_pair("code", &code)
                            .append_pair("state", &state)
                            .finish();
                        let gh_res = try_map!(
                            client
                                .post("https://github.com/login/oauth/access_token")
                                .body(gh_req.as_bytes())
                                .headers(headers)
                                .send(),
                            ErrGithubToken, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubToken {
                            access_token: String,
                        }
                        let gh_token: GithubToken = try_map!(
                            json_from_reader(gh_res),
                            BadGithubTokenResult, res
                        );
                        let gh_token = gh_token.access_token;
                        // Now that we're actually authed as this user, we need their name.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/user?access_token={}", gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubUsername, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubUser {
                            login: String,
                        }
                        let gh_user: GithubUser = try_map!(
                            json_from_reader(gh_res),
                            BadGithubUsernameResult, res
                        );
                        let gh_user = gh_user.login;
                        // Note: the user may need to request (or give) this app org perms to do this.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/org/{}/members/{}?access_token={}", org, gh_user, gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubOrgCheck, res
                        );
                        if gh_res.status.is_success() {
                            // Awesome: we're authenticated!
                            let auth = generate_sha1_hmac(
                                self.secret.as_bytes(),
                                gh_user.as_bytes()
                            ).to_hex();
                            res.headers_mut().set(SetCookie(vec![
                                CookiePair::new("auth".to_owned(), auth),
                                CookiePair::new("name".to_owned(), gh_user)
                            ]));
                            res.headers_mut().set(Location(".".to_owned()));
                            *res.status_mut() = StatusCode::Found;
                        } else {
                            warn!("Got Github user that's not member: {:?}", gh_user);
                            *res.status_mut() = StatusCode::Forbidden;
                        }
                    } else {
                        let state = generate_sha1_hmac(
                            self.secret.as_bytes(),
                            req.remote_addr.ip().to_string().as_bytes()
                        ).to_hex();
                        res.headers_mut().set(Location(
                            format!(
                                "https://github.com/login/oauth/authorize?client_id={}&state={}&scope=read:org",
                                app_id,
                                state
                            )
                        ));
                        *res.status_mut() = StatusCode::Found;
                    }
                    CheckResult::NotAuthenticated
                } else {
                    CheckResult::Authenticated(req, res)
                }
            }
        }
    }
}<commit_msg>Fix org thing<commit_after>// This file is released under the same terms as Rust itself.

use hex::{FromHex, FromHexError, ToHex};
use hyper;
use hyper::client::Client;
use hyper::header::{Accept, ContentType, Cookie, CookiePair, Headers, Location, SetCookie, UserAgent, qitem};
use hyper::mime::{Mime, TopLevel, SubLevel, Attr, Value};
use hyper::server::{Request, Response};
use hyper::status::StatusCode;
use hyper::uri::RequestUri;
use serde_json::{self, from_reader as json_from_reader};
use std::convert::AsRef;
use url::form_urlencoded;
use util::USER_AGENT;
use util::crypto::{generate_sha1_hmac, verify_sha1_hmac};

pub enum Auth {
    None,
    Github(String, String, String),
}

#[derive(Clone, Copy)]
pub enum AuthRef<'a> {
    None,
    Github(&'a str, &'a str, &'a str),
}

impl<'a> From<&'a Auth> for AuthRef<'a> {
    fn from(a: &'a Auth) -> AuthRef<'a> {
        match *a {
            Auth::None => AuthRef::None,
            Auth::Github(ref app_id, ref app_secret, ref org) =>
                AuthRef::Github(app_id.as_ref(), app_secret.as_ref(), org.as_ref()),
        }
    }
}

pub struct AuthManager<'a> {
    pub auth: AuthRef<'a>,
    pub secret: &'a str,
}

pub enum CheckResult<'a, 'b: 'a, 'c> {
    Authenticated(Request<'a, 'b>, Response<'c>),
    NotAuthenticated,
    Err(CheckResultError),
}

quick_error!{
    #[derive(Debug)]
    pub enum CheckResultError {
        ErrGithubToken(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubTokenResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubUsername(e: hyper::error::Error) {
            cause(e)
        }
        BadGithubUsernameResult(e: serde_json::error::Error) {
            cause(e)
        }
        ErrGithubOrgCheck(e: hyper::error::Error) {
            cause(e)
        }
        InvalidGithubStateParam(e: FromHexError) {
            cause(e)
        }
        BadGithubStateParam {}
        MissingGithubParam {}
    }
}

impl<'a> AuthManager<'a> {
    pub fn check<'b, 'c, 'd>(
        &self,
        req: Request<'b, 'c>,
        mut res: Response<'d>,
    ) -> CheckResult<'b, 'c, 'd> {
        macro_rules! try_map {
            ($e: expr, $i: ident, $res: ident) => (
                match $e {
                    Ok(x) => x,
                    Err(e) => {
                        *$res.status_mut() = StatusCode::InternalServerError;
                        return CheckResult::Err(CheckResultError::$i(e));
                    }
                }
            )
        }
        match self.auth {
            AuthRef::None => {
                CheckResult::Authenticated(req, res)
            }
            AuthRef::Github(app_id, app_secret, org) => {
                let authed = req.headers.get::<Cookie>()
                    .and_then(|x| {
                        let mut auth = None;
                        let mut name = None;
                        for cookie in x.iter() {
                            match &cookie.name[..] {
                                "auth" => auth = Some(cookie.value.clone()),
                                "name" => name = Some(cookie.value.clone()),
                                n => {
                                    warn!("Unrecognized cookie {}", n);
                                }
                            }
                        }
                        if let (Some(auth), Some(name)) = (auth, name) {
                            Some((auth, name))
                        } else {
                            None
                        }
                    })
                    .map(|(auth, name)| {
                        match Vec::from_hex(&auth) {
                            Ok(auth) => {
                                verify_sha1_hmac(
                                    self.secret.as_bytes(),
                                    name.as_bytes(),
                                    &auth
                                )
                            }
                            Err(e) => {
                                warn!("Bad auth \"{}\": {:?}", auth, e);
                                false
                            }
                        }
                    })
                    .unwrap_or(false);
                if !authed {
                    let path = if let RequestUri::AbsolutePath(ref path) = req.uri {
                        path
                    } else {
                        *res.status_mut() = StatusCode::BadRequest;
                        return CheckResult::NotAuthenticated;
                    };
                    let path = path.as_bytes();
                    if path.len() > 11 && &path[..11] == b"/_gh_login?" {
                        let query = &path[11..];
                        let query = form_urlencoded::parse(query);
                        let mut code = None;
                        let mut state = None;
                        for param in query {
                            match param.0.as_ref() {
                                "state" => state = Some(param.1.into_owned()),
                                "code" => code = Some(param.1.into_owned()),
                                _ => {
                                    warn!(
                                        "Unrecognized Github oAuth param"
                                    );
                                }
                            }
                        }
                        let (code, state) = if let (Some(code), Some(state)) = (code, state) {
                            (code, state)
                        } else {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::MissingGithubParam);
                        };
                        if !verify_sha1_hmac(
                            self.secret.as_bytes(),
                            &req.remote_addr.ip().to_string().as_bytes(),
                            &try_map!(
                                Vec::from_hex(&state),
                                InvalidGithubStateParam,
                                res
                            )
                        ) {
                            *res.status_mut() = StatusCode::BadRequest;
                            return CheckResult::Err(CheckResultError::BadGithubStateParam);
                        }
                        let client = Client::default();
                        // We got successfully authed to GitHub
                        // But we need to check if we're in the org
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(ContentType::form_url_encoded());
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![])),
                        ]));
                        let gh_req = form_urlencoded::Serializer::new(String::new())
                            .append_pair("client_id", app_id)
                            .append_pair("client_secret", app_secret)
                            .append_pair("code", &code)
                            .append_pair("state", &state)
                            .finish();
                        let gh_res = try_map!(
                            client
                                .post("https://github.com/login/oauth/access_token")
                                .body(gh_req.as_bytes())
                                .headers(headers)
                                .send(),
                            ErrGithubToken, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubToken {
                            access_token: String,
                        }
                        let gh_token: GithubToken = try_map!(
                            json_from_reader(gh_res),
                            BadGithubTokenResult, res
                        );
                        let gh_token = gh_token.access_token;
                        // Now that we're actually authed as this user, we need their name.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/user?access_token={}", gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubUsername, res
                        );
                        #[derive(Serialize, Deserialize)]
                        struct GithubUser {
                            login: String,
                        }
                        let gh_user: GithubUser = try_map!(
                            json_from_reader(gh_res),
                            BadGithubUsernameResult, res
                        );
                        let gh_user = gh_user.login;
                        // Note: the user may need to request (or give) this app org perms to do this.
                        let mut headers = Headers::new();
                        headers.set(UserAgent(USER_AGENT.to_owned()));
                        headers.set(Accept(vec![
                            qitem(Mime(TopLevel::Application, SubLevel::Json,
                                       vec![(Attr::Charset, Value::Utf8)])),
                        ]));
                        let gh_res = try_map!(
                            client
                                .get(&format!("https://api.github.com/orgs/{}/members/{}?access_token={}", org, gh_user, gh_token))
                                .headers(headers)
                                .send(),
                            ErrGithubOrgCheck, res
                        );
                        if gh_res.status.is_success() {
                            // Awesome: we're authenticated!
                            let auth = generate_sha1_hmac(
                                self.secret.as_bytes(),
                                gh_user.as_bytes()
                            ).to_hex();
                            res.headers_mut().set(SetCookie(vec![
                                CookiePair::new("auth".to_owned(), auth),
                                CookiePair::new("name".to_owned(), gh_user)
                            ]));
                            res.headers_mut().set(Location(".".to_owned()));
                            *res.status_mut() = StatusCode::Found;
                        } else {
                            warn!("Got Github user that's not member: {:?}", gh_user);
                            *res.status_mut() = StatusCode::Forbidden;
                        }
                    } else {
                        let state = generate_sha1_hmac(
                            self.secret.as_bytes(),
                            req.remote_addr.ip().to_string().as_bytes()
                        ).to_hex();
                        res.headers_mut().set(Location(
                            format!(
                                "https://github.com/login/oauth/authorize?client_id={}&state={}&scope=read:org",
                                app_id,
                                state
                            )
                        ));
                        *res.status_mut() = StatusCode::Found;
                    }
                    CheckResult::NotAuthenticated
                } else {
                    CheckResult::Authenticated(req, res)
                }
            }
        }
    }
}<|endoftext|>


<commit_before>use futures::future;
use futures::Future;
use hyper;
use hyper::Uri;
use hyper::client::{Client, Request};
use mktemp::Temp;
use slog;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
use tokio_core::reactor::{Handle, Remote};
use tokio_process::CommandExt;
use tera::Tera;

use http::*;
use papers::DocumentSpec;
use error::{Error, ErrorKind};

pub struct Workspace {
    dir: Temp,
    document_spec: DocumentSpec,
    handle: Handle,
    template_path: ::std::path::PathBuf,
    logger: slog::Logger,
}

struct WorkspaceContext {
    handle: Handle,
    logger: slog::Logger,
    temp_dir_path: ::std::path::PathBuf,
}

/// We ignore file names that end with a slash for now, and always determine the file name from the Uri
/// TODO: investigate Content-Disposition: attachment
fn extract_file_name_from_uri(uri: &Uri) -> Option<String> {
    match uri.path().split('/').last() {
        Some(name) => {
            if !name.is_empty() {
                Some(name.to_string())
            } else {
                None
            }
        },
        None => None,
    }
}

/// Since `mktemp::Temp` implements Drop by deleting the directory, we don't need to worry about
/// leaving files or directories behind. On the flipside, we must ensure it is not dropped before
/// the last returned future that needs the directory finishes.
impl Workspace {

    pub fn new(remote: Remote, document_spec: DocumentSpec, logger: slog::Logger) -> Result<Workspace, Error> {
        let dir = Temp::new_dir()?;
        let mut template_path = dir.to_path_buf();
        template_path.push(Path::new(&document_spec.output_file_name.replace("pdf", "tex")));
        Ok(Workspace {
            document_spec: document_spec,
            handle: remote.handle().unwrap(),
            template_path: template_path,
            dir,
            logger,
        })
    }

    pub fn preview(self) -> Box<Future<Item=String, Error=Error>> {
        let Workspace {
            handle,
            document_spec,
            ..
        } = self;

        let DocumentSpec {
            template_url,
            variables,
            ..
        } = document_spec;

        // Download the template and populate it
        let work = Client::configure()
            .connector(https_connector(&handle.clone()))
            .build(&handle.clone())
            .get_follow_redirect(&template_url.0)
            .and_then(|res| res.get_body_bytes())
            .and_then(|bytes| {
                ::std::string::String::from_utf8(bytes).map_err(Error::from)
            }).and_then(move |template_string| {
                Tera::one_off(&template_string, &variables, false).map_err(Error::from)
            });

        Box::new(work)
    }

    pub fn execute(self) -> Box<Future<Item=(), Error=()>> {
        let Workspace {
            handle,
            document_spec,
            template_path,
            dir,
            logger,
        } = self;

        let DocumentSpec {
            assets_urls,
            callback_url,
            template_url,
            output_file_name,
            variables,
        } = document_spec;

        debug!(logger, "Starting Workspace worker");

        let context = WorkspaceContext {
            handle,
            logger,
            temp_dir_path: dir.to_path_buf(),
        };

        let error_logger = context.logger.clone();
        let error_path_handle = context.handle.clone();
        let error_path_callback_url = callback_url.0.clone();

        // First download the template and populate it
        let work = Client::configure()
            .connector(https_connector(&context.handle.clone()))
            .build(&context.handle.clone())
            .get_follow_redirect(&template_url.0)
            .and_then(|res| res.get_body_bytes())
            .and_then(|bytes| {
                debug!(context.logger, "Successfully downloaded the template");
                String::from_utf8(bytes)
                    .map_err(Error::from)
                    .map(|template_string| (context, template_string))
            }).and_then(move |(context, template_string)| {
                Tera::one_off(&template_string, &variables, false)
                    .map_err(Error::from)
                    .map(|latex_string| (context, latex_string))
            }).and_then(|(context, latex_string)| {
                debug!(context.logger, "Writing template to {:?}", template_path.clone());
                let mut file = ::std::fs::File::create(template_path.clone()).unwrap();
                file.write_all(latex_string.as_bytes()).expect("could not write latex file");
                debug!(context.logger, "Template successfully written to {:?}", template_path.clone());
                Ok((context, template_path))
            })

        // Then download the assets and save them in the temporary directory
                .and_then(move |(context, template_path)| {
                    let inner_handle = context.handle.clone();
                    let inner_temp_dir_path = context.temp_dir_path.clone();
                    debug!(context.logger, "Downloading assets {:?}", assets_urls);
                    let futures = assets_urls.into_iter().map(move |uri| {
                        let mut path = inner_temp_dir_path.clone();
                        Client::configure()
                            .connector(https_connector(&inner_handle.clone()))
                            .build(&inner_handle.clone())
                            .get_follow_redirect(&uri.0)
                            .map(move |res| (res, uri.0))
                            .and_then(move |(res, uri)| {
                                let file_name = res.file_name().take();
                                res.get_body_bytes().map(|bytes| (bytes, file_name, uri))
                            }).and_then(move |(bytes, file_name, uri)| {
                                let file_name = file_name.or_else(|| extract_file_name_from_uri(&uri));
                                if let Some(file_name) = file_name {
                                    path.push(file_name);
                                    ::std::fs::File::create(&path)
                                        .and_then(|mut file| file.write_all(&bytes))
                                        .map(|_| ())
                                        .map_err(Error::from)
                                } else {
                                    Ok(())
                                }
                            })
                    });
                    future::join_all(futures).map(|result| (context, template_path, result))
                })

                // Then run latex
                .and_then(move |(context, template_path, _)| {
                    let inner_handle = context.handle.clone();
                    Command::new("xelatex")
                        .arg(&format!("-output-directory={}", &context.temp_dir_path.to_str().unwrap()))
                        .arg("-interaction=nonstopmode")
                        .arg("-file-line-error")
                        .arg(template_path.clone())
                        .output_async(&inner_handle)
                        .map(|output| (context, output))
                        .map_err(Error::from)
                }).and_then(|(context, output)| {
                    let stdout = String::from_utf8(output.stdout).unwrap();
                    if output.status.success() {
                        debug!(context.logger, "{}", stdout);
                        Ok(context)
                    } else {
                        Err(ErrorKind::LatexFailed(stdout).into())
                    }
                })

                // Then construct the path to the generated PDF
                .map(move |context| {
                    let mut path = context.temp_dir_path.clone();
                    path.push(Path::new(&output_file_name));
                    (context, path)
                })

                // Then get a multipart request from the generated PDF
                .and_then(move |(context, pdf_path)| {
                    debug!(context.logger, "Reading the pdf from {:?}", pdf_path);
                    debug!(context.logger, "Sending generated PDF to {}", callback_url.0);
                    multipart_request_with_file(
                        Request::new(hyper::Method::Post, callback_url.0),
                        pdf_path
                    ).map(|r| (context, r))
                })

                // Finally, post the PDF to the callback URL
                .and_then(move |(context, request)| {
                    // Avoid dir being dropped early
                    let _dir = dir;
                    Client::configure()
                        .connector(https_connector(&context.handle.clone()))
                        .build(&context.handle.clone())
                        .request(request)
                        .map(|response| (context, response))
                        .map_err(Error::from)
                }).and_then(|(context, response)| {
                    info!(
                        context.logger,
                        "Callback response: {}",
                        response.status().canonical_reason().unwrap_or("unknown")
                    );

                    response
                        .get_body_bytes()
                        .map(|bytes| (context, bytes))

                }).and_then(|(context, bytes)| {
                    debug!(
                        context.logger,
                        "Callback response body: {:?}",
                        ::std::str::from_utf8(&bytes).unwrap_or("<binary content>")
                    );
                    future::ok(())
                })

                // Report errors to the callback url
                .or_else(move |error| {
                    error!(error_logger, format!("{}", error));
                    let req = Request::new(hyper::Method::Post, error_path_callback_url);
                    Client::new(&error_path_handle)
                        .request(multipart_request_with_error(req, &error).unwrap())
                        .map(|_| ())
                });

        Box::new(work.map_err(|_| ()))
    }
}

#[cfg(test)]
mod tests {
    use hyper::Uri;
    use super::extract_file_name_from_uri;

    #[test]
    fn test_extract_file_name_from_uri_works() {
        let assert_extracted = |input: &'static str, expected_output: Option<&'static str>| {
            let uri = input.parse::<Uri>().unwrap();
            assert_eq!(extract_file_name_from_uri(&uri), expected_output.map(|o| o.to_string()));
        };

        assert_extracted("/logo.png", Some("logo.png"));
        assert_extracted("/assets/", None);
        assert_extracted("/assets/icon", Some("icon"));
        assert_extracted("/", None);
        assert_extracted("http://www.store2be.com", None);
    }
}
<commit_msg>✨ Add debug logging of document specs<commit_after>use futures::future;
use futures::Future;
use hyper;
use hyper::Uri;
use hyper::client::{Client, Request};
use mktemp::Temp;
use slog;
use std::io::prelude::*;
use std::path::Path;
use std::process::Command;
use tokio_core::reactor::{Handle, Remote};
use tokio_process::CommandExt;
use tera::Tera;

use http::*;
use papers::DocumentSpec;
use error::{Error, ErrorKind};

pub struct Workspace {
    dir: Temp,
    document_spec: DocumentSpec,
    handle: Handle,
    template_path: ::std::path::PathBuf,
    logger: slog::Logger,
}

struct WorkspaceContext {
    handle: Handle,
    logger: slog::Logger,
    temp_dir_path: ::std::path::PathBuf,
}

/// We ignore file names that end with a slash for now, and always determine the file name from the Uri
/// TODO: investigate Content-Disposition: attachment
fn extract_file_name_from_uri(uri: &Uri) -> Option<String> {
    match uri.path().split('/').last() {
        Some(name) => {
            if !name.is_empty() {
                Some(name.to_string())
            } else {
                None
            }
        },
        None => None,
    }
}

/// Since `mktemp::Temp` implements Drop by deleting the directory, we don't need to worry about
/// leaving files or directories behind. On the flipside, we must ensure it is not dropped before
/// the last returned future that needs the directory finishes.
impl Workspace {

    pub fn new(remote: Remote, document_spec: DocumentSpec, logger: slog::Logger) -> Result<Workspace, Error> {
        let dir = Temp::new_dir()?;
        let mut template_path = dir.to_path_buf();
        template_path.push(Path::new(&document_spec.output_file_name.replace("pdf", "tex")));
        Ok(Workspace {
            document_spec: document_spec,
            handle: remote.handle().unwrap(),
            template_path: template_path,
            dir,
            logger,
        })
    }

    pub fn preview(self) -> Box<Future<Item=String, Error=Error>> {
        let Workspace {
            handle,
            document_spec,
            ..
        } = self;

        let DocumentSpec {
            template_url,
            variables,
            ..
        } = document_spec;

        // Download the template and populate it
        let work = Client::configure()
            .connector(https_connector(&handle.clone()))
            .build(&handle.clone())
            .get_follow_redirect(&template_url.0)
            .and_then(|res| res.get_body_bytes())
            .and_then(|bytes| {
                ::std::string::String::from_utf8(bytes).map_err(Error::from)
            }).and_then(move |template_string| {
                Tera::one_off(&template_string, &variables, false).map_err(Error::from)
            });

        Box::new(work)
    }

    pub fn execute(self) -> Box<Future<Item=(), Error=()>> {
        let Workspace {
            handle,
            document_spec,
            template_path,
            dir,
            logger,
        } = self;

        debug!(logger, "Trying to generate PDF with document spec: {:?}", document_spec);

        let DocumentSpec {
            assets_urls,
            callback_url,
            template_url,
            output_file_name,
            variables,
        } = document_spec;

        debug!(logger, "Starting Workspace worker");

        let context = WorkspaceContext {
            handle,
            logger,
            temp_dir_path: dir.to_path_buf(),
        };

        let error_logger = context.logger.clone();
        let error_path_handle = context.handle.clone();
        let error_path_callback_url = callback_url.0.clone();

        // First download the template and populate it
        let work = Client::configure()
            .connector(https_connector(&context.handle.clone()))
            .build(&context.handle.clone())
            .get_follow_redirect(&template_url.0)
            .and_then(|res| res.get_body_bytes())
            .and_then(|bytes| {
                debug!(context.logger, "Successfully downloaded the template");
                String::from_utf8(bytes)
                    .map_err(Error::from)
                    .map(|template_string| (context, template_string))
            }).and_then(move |(context, template_string)| {
                Tera::one_off(&template_string, &variables, false)
                    .map_err(Error::from)
                    .map(|latex_string| (context, latex_string))
            }).and_then(|(context, latex_string)| {
                debug!(context.logger, "Writing template to {:?}", template_path.clone());
                let mut file = ::std::fs::File::create(template_path.clone()).unwrap();
                file.write_all(latex_string.as_bytes()).expect("could not write latex file");
                debug!(context.logger, "Template successfully written to {:?}", template_path.clone());
                Ok((context, template_path))
            })

        // Then download the assets and save them in the temporary directory
                .and_then(move |(context, template_path)| {
                    let inner_handle = context.handle.clone();
                    let inner_temp_dir_path = context.temp_dir_path.clone();
                    debug!(context.logger, "Downloading assets {:?}", assets_urls);
                    let futures = assets_urls.into_iter().map(move |uri| {
                        let mut path = inner_temp_dir_path.clone();
                        Client::configure()
                            .connector(https_connector(&inner_handle.clone()))
                            .build(&inner_handle.clone())
                            .get_follow_redirect(&uri.0)
                            .map(move |res| (res, uri.0))
                            .and_then(move |(res, uri)| {
                                let file_name = res.file_name().take();
                                res.get_body_bytes().map(|bytes| (bytes, file_name, uri))
                            }).and_then(move |(bytes, file_name, uri)| {
                                let file_name = file_name.or_else(|| extract_file_name_from_uri(&uri));
                                if let Some(file_name) = file_name {
                                    path.push(file_name);
                                    ::std::fs::File::create(&path)
                                        .and_then(|mut file| file.write_all(&bytes))
                                        .map(|_| ())
                                        .map_err(Error::from)
                                } else {
                                    Ok(())
                                }
                            })
                    });
                    future::join_all(futures).map(|result| (context, template_path, result))
                })

                // Then run latex
                .and_then(move |(context, template_path, _)| {
                    let inner_handle = context.handle.clone();
                    Command::new("xelatex")
                        .arg(&format!("-output-directory={}", &context.temp_dir_path.to_str().unwrap()))
                        .arg("-interaction=nonstopmode")
                        .arg("-file-line-error")
                        .arg(template_path.clone())
                        .output_async(&inner_handle)
                        .map(|output| (context, output))
                        .map_err(Error::from)
                }).and_then(|(context, output)| {
                    let stdout = String::from_utf8(output.stdout).unwrap();
                    if output.status.success() {
                        debug!(context.logger, "{}", stdout);
                        Ok(context)
                    } else {
                        Err(ErrorKind::LatexFailed(stdout).into())
                    }
                })

                // Then construct the path to the generated PDF
                .map(move |context| {
                    let mut path = context.temp_dir_path.clone();
                    path.push(Path::new(&output_file_name));
                    (context, path)
                })

                // Then get a multipart request from the generated PDF
                .and_then(move |(context, pdf_path)| {
                    debug!(context.logger, "Reading the pdf from {:?}", pdf_path);
                    debug!(context.logger, "Sending generated PDF to {}", callback_url.0);
                    multipart_request_with_file(
                        Request::new(hyper::Method::Post, callback_url.0),
                        pdf_path
                    ).map(|r| (context, r))
                })

                // Finally, post the PDF to the callback URL
                .and_then(move |(context, request)| {
                    // Avoid dir being dropped early
                    let _dir = dir;
                    Client::configure()
                        .connector(https_connector(&context.handle.clone()))
                        .build(&context.handle.clone())
                        .request(request)
                        .map(|response| (context, response))
                        .map_err(Error::from)
                }).and_then(|(context, response)| {
                    info!(
                        context.logger,
                        "Callback response: {}",
                        response.status().canonical_reason().unwrap_or("unknown")
                    );

                    response
                        .get_body_bytes()
                        .map(|bytes| (context, bytes))

                }).and_then(|(context, bytes)| {
                    debug!(
                        context.logger,
                        "Callback response body: {:?}",
                        ::std::str::from_utf8(&bytes).unwrap_or("<binary content>")
                    );
                    future::ok(())
                })

                // Report errors to the callback url
                .or_else(move |error| {
                    error!(error_logger, format!("{}", error));
                    let req = Request::new(hyper::Method::Post, error_path_callback_url);
                    Client::new(&error_path_handle)
                        .request(multipart_request_with_error(req, &error).unwrap())
                        .map(|_| ())
                });

        Box::new(work.map_err(|_| ()))
    }
}

#[cfg(test)]
mod tests {
    use hyper::Uri;
    use super::extract_file_name_from_uri;

    #[test]
    fn test_extract_file_name_from_uri_works() {
        let assert_extracted = |input: &'static str, expected_output: Option<&'static str>| {
            let uri = input.parse::<Uri>().unwrap();
            assert_eq!(extract_file_name_from_uri(&uri), expected_output.map(|o| o.to_string()));
        };

        assert_extracted("/logo.png", Some("logo.png"));
        assert_extracted("/assets/", None);
        assert_extracted("/assets/icon", Some("icon"));
        assert_extracted("/", None);
        assert_extracted("http://www.store2be.com", None);
    }
}
<|endoftext|>


<commit_before>use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};

use self::map::Map;

mod map;

pub struct World {
    map: Map,
    iterations: u64,
}

impl Display for World {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}\n{:?}, iterations: {}", self.map, self.map.dim(), self.iterations)
    }
}

impl World {
    pub fn load(filename: &str) -> Result<Self, Box<Error>> {
        use std::fs::File;
        use std::io::Read;

        let mut file = File::open(filename)?;
        let mut map = String::new();
        file.read_to_string(&mut map)?;
        map.parse().map(World::new).map_err(Into::into)
    }

    pub fn next(&mut self) {
        self.iterations += 1;
        self.map.next();
    }
}

impl World {
    fn new(map: Map) -> Self {
        World { map, iterations: 0 }
    }
}
<commit_msg>read_to_string is stable (1.26)<commit_after>use std::error::Error;
use std::fmt::{Display, Formatter, Result as FmtResult};

use self::map::Map;

mod map;

pub struct World {
    map: Map,
    iterations: u64,
}

impl Display for World {
    fn fmt(&self, f: &mut Formatter) -> FmtResult {
        write!(f, "{}\n{:?}, iterations: {}", self.map, self.map.dim(), self.iterations)
    }
}

impl World {
    pub fn load(filename: &str) -> Result<Self, Box<Error>> {
        let map = ::std::fs::read_to_string(filename)?.parse()?;
        Ok(World::new(map))
    }

    pub fn next(&mut self) {
        self.iterations += 1;
        self.map.next();
    }
}

impl World {
    fn new(map: Map) -> Self {
        World { map, iterations: 0 }
    }
}
<|endoftext|>


<commit_before>use image::{ImageBuffer, RgbImage};
use bitreader::{BitReader};
use itertools::Itertools;
use discrete_transforms::dct_2d::Dct2D;

#[derive(Debug)]
pub struct CoverImage {
    cover_image: RgbImage,
    message: &'static str,
    output_image_path: &'static str,
    message_as_bits: Vec<u8>,
    tiles: Vec<u8>,
    dct_tiles: Vec<f64>,
    modified_pixels: Vec<f64>
}

impl CoverImage {
    pub fn new() -> CoverImage {
        CoverImage {
            cover_image: ImageBuffer::new(0, 0),
            message: "",
            output_image_path: "",
            message_as_bits: vec![],
            tiles: vec![],
            dct_tiles: vec![],
            modified_pixels: vec![]
        }
    }

    pub fn set_cover_image(&mut self, input_path: &'static str) -> &mut Self {
        self.cover_image = image::open(input_path).unwrap().as_rgb8().unwrap().clone();

        self
    }

    pub fn set_message(&mut self, message: &'static str) -> &mut Self {
        self.message = message;

        self
    }

    pub fn set_output_image_path(&mut self, output_path: &'static str) -> &mut Self {
        self.output_image_path = output_path;

        self
    }

    pub fn encode(&mut self) {
        self.get_message_as_bits();
        self.tile_image();
        self.encode_message();

        self.cover_image.save(self.output_image_path).unwrap();
    }

    fn get_message_as_bits(&mut self) {
        let mut reader = BitReader::new(&self.message.as_bytes());

        for _ in 0..self.message.len() * 8 {
            self.message_as_bits.push(reader.read_u8(1).unwrap());
        }
    }

    fn tile_image(&mut self) {
        let (width, height) = self.cover_image.dimensions();
        let mut count = 0;

        for row_index in 0..(height / 8) as u32 {
            for col_index in 0..(width / 8) as u32 {
                for channel in 0..3 {
                    if count == self.message_as_bits.len() {
                        break
                    }

                    for row in 0..8 {
                        for column in 0..8 {
                            let pixel = self.cover_image.get_pixel(column + (col_index * 8),
                                                                      row + (row_index * 8));

                            self.tiles.push(pixel.data[channel]);
                        }
                    }

                    count += 1;
                }
            }
        }
    }

    fn encode_message(&mut self) {
        let mut dct = Dct2D::new();

        for chunk in &self.tiles.iter().chunks(64) {
            let input = chunk.map(|x| *x as f64).collect_vec();

            dct.set_input(input);

            self.dct_tiles.extend(&dct.forward());
        }
    }
}

#[test]
fn get_message_as_bits_test() {
    let test_bits = vec![0, 1, 1, 0, 1, 0, 0, 0];
    let mut cover_image = CoverImage::new();

    cover_image.set_message("h");
    cover_image.get_message_as_bits();

    assert_eq!(cover_image.message_as_bits, test_bits);
}

#[test]
fn tile_image_length_test() {
    let mut cover_image = CoverImage::new();

    cover_image.set_message("h");
    cover_image.set_cover_image("src/testing.jpg");

    // 64 = length of 1 tile, message.len() * 8 = number of tiles needed; 1 tile per bit
    let length = 64 * cover_image.message.len() * 8;

    cover_image.get_message_as_bits();
    cover_image.tile_image();

    let tile_vec_length = cover_image.tiles.len();

    assert_eq!(length, tile_vec_length);
}

#[test]
fn test_random_pixel_in_tile() {
    let mut cover_image = CoverImage::new();

    cover_image.set_message("h");
    cover_image.set_cover_image("src/testing.jpg");
    cover_image.get_message_as_bits();
    cover_image.tile_image();

    let pixel = cover_image.cover_image.get_pixel(8, 8);

    let r_pixel = pixel.data[0];
    let g_pixel = pixel.data[1];
    let b_pixel = pixel.data[2];

    let r_pixel_from_tile = cover_image.tiles[63];
    let g_pixel_from_tile = cover_image.tiles[127];
    let b_pixel_from_tile = cover_image.tiles[191];

    assert_eq!(r_pixel, r_pixel_from_tile);
    assert_eq!(g_pixel, g_pixel_from_tile);
    assert_eq!(b_pixel, b_pixel_from_tile);
}

#[test]
fn delete() {
    let mut cover_image = CoverImage::new();

    cover_image.set_message("h");
    cover_image.set_cover_image("src/testing.jpg");
    cover_image.set_output_image_path("output.jpg");
    cover_image.encode();
}<commit_msg>Add quantization step<commit_after>use image::{ImageBuffer, RgbImage};
use bitreader::{BitReader};
use itertools::Itertools;
use discrete_transforms::dct_2d::Dct2D;

#[derive(Debug)]
pub struct CoverImage {
    cover_image: RgbImage,
    message: &'static str,
    output_image_path: &'static str,
    message_as_bits: Vec<u8>,
    tiles: Vec<u8>,
    dct_tiles: Vec<f64>,
    quantized_tiles: Vec<i32>,
    modified_pixels: Vec<f64>
}

impl CoverImage {
    pub fn new() -> CoverImage {
        CoverImage {
            cover_image: ImageBuffer::new(0, 0),
            message: "",
            output_image_path: "",
            message_as_bits: vec![],
            tiles: vec![],
            dct_tiles: vec![],
            quantized_tiles: vec![],
            modified_pixels: vec![]
        }
    }

    pub fn set_cover_image(&mut self, input_path: &'static str) -> &mut Self {
        self.cover_image = image::open(input_path).unwrap().as_rgb8().unwrap().clone();

        self
    }

    pub fn set_message(&mut self, message: &'static str) -> &mut Self {
        self.message = message;

        self
    }

    pub fn set_output_image_path(&mut self, output_path: &'static str) -> &mut Self {
        self.output_image_path = output_path;

        self
    }

    pub fn encode(&mut self) {
        self.get_message_as_bits();
        self.tile_image();
        self.encode_message();

        self.cover_image.save(self.output_image_path).unwrap();
    }

    fn get_message_as_bits(&mut self) {
        let mut reader = BitReader::new(&self.message.as_bytes());

        for _ in 0..self.message.len() * 8 {
            self.message_as_bits.push(reader.read_u8(1).unwrap());
        }
    }

    fn tile_image(&mut self) {
        let (width, height) = self.cover_image.dimensions();
        let mut count = 0;

        for row_index in 0..(height / 8) as u32 {
            for col_index in 0..(width / 8) as u32 {
                for channel in 0..3 {
                    if count == self.message_as_bits.len() {
                        break
                    }

                    for row in 0..8 {
                        for column in 0..8 {
                            let pixel = self.cover_image.get_pixel(column + (col_index * 8),
                                                                      row + (row_index * 8));

                            self.tiles.push(pixel.data[channel]);
                        }
                    }

                    count += 1;
                }
            }
        }
    }

    fn encode_message(&mut self) {
        let mut dct = Dct2D::new();

        for chunk in &self.tiles.iter().chunks(64) {
            let input = chunk.map(|x| *x as f64).collect_vec();

            dct.set_input(input);

            self.dct_tiles.extend(&dct.forward());
        }

        self.quantize();
    }

    fn quantize(&mut self) {
        let quantization_table = vec![16, 11, 10, 16,  24,  40,  51,  61,
                                      12, 12, 14, 19,  26,  58,  60,  55,
                                      14, 13, 16, 24,  40,  57,  69,  56,
                                      14, 17, 22, 29,  51,  87,  80,  62,
                                      18, 22, 37, 56,  68, 109, 103,  77,
                                      24, 36, 55, 64,  81, 104, 113,  92,
                                      49, 64, 78, 87, 103, 121, 120, 101,
                                      72, 92, 95, 98, 112, 100, 103,  99];

        for x in 0..self.dct_tiles.len() {
            let quantized_coeff = self.dct_tiles[x] / quantization_table[x % 64] as f64;

            self.quantized_tiles.push(quantized_coeff as i32);
        }
    }
}

#[test]
fn get_message_as_bits_test() {
    let test_bits = vec![0, 1, 1, 0, 1, 0, 0, 0];
    let mut cover_image = CoverImage::new();

    cover_image.set_message("h");
    cover_image.get_message_as_bits();

    assert_eq!(cover_image.message_as_bits, test_bits);
}

#[test]
fn tile_image_length_test() {
    let mut cover_image = CoverImage::new();

    cover_image.set_message("h");
    cover_image.set_cover_image("src/testing.jpg");

    // 64 = length of 1 tile, message.len() * 8 = number of tiles needed; 1 tile per bit
    let length = 64 * cover_image.message.len() * 8;

    cover_image.get_message_as_bits();
    cover_image.tile_image();

    let tile_vec_length = cover_image.tiles.len();

    assert_eq!(length, tile_vec_length);
}

#[test]
fn test_random_pixel_in_tile() {
    let mut cover_image = CoverImage::new();

    cover_image.set_message("h");
    cover_image.set_cover_image("src/testing.jpg");
    cover_image.get_message_as_bits();
    cover_image.tile_image();

    let pixel = cover_image.cover_image.get_pixel(8, 8);

    let r_pixel = pixel.data[0];
    let g_pixel = pixel.data[1];
    let b_pixel = pixel.data[2];

    let r_pixel_from_tile = cover_image.tiles[63];
    let g_pixel_from_tile = cover_image.tiles[127];
    let b_pixel_from_tile = cover_image.tiles[191];

    assert_eq!(r_pixel, r_pixel_from_tile);
    assert_eq!(g_pixel, g_pixel_from_tile);
    assert_eq!(b_pixel, b_pixel_from_tile);
}

#[test]
fn delete() {
    let mut cover_image = CoverImage::new();

    cover_image.set_message("h");
    cover_image.set_cover_image("src/testing.jpg");
    cover_image.set_output_image_path("output.jpg");
    cover_image.encode();
}<|endoftext|>


<commit_before><commit_msg>Remove redundant 'resolve_obligations_as_possible' call<commit_after><|endoftext|>


<commit_before>extern crate orbclient;

use std::env;
use std::fs::{self, File};
use std::process::Command;
use std::thread;

use orbclient::{BmpFile, Color, EventOption, Window};

use package::Package;

pub mod package;

fn get_packages() -> Vec<Package> {
    let mut packages: Vec<Package> = Vec::new();

    for entry_result in fs::read_dir("/apps/").unwrap() {
        let entry = entry_result.unwrap();
        if entry.file_type().unwrap().is_dir() {
            packages.push(Package::from_path(&("/apps/".to_string() + entry.file_name().to_str().unwrap())));
        }
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, shutdown: &BmpFile, mouse_x: i32, mouse_y: i32){
    window.set(Color::rgba(0, 0, 0, 0));
    let w = window.width();
    window.rect(0, 16, w, 32, Color::rgba(0, 0, 0, 128));

    let mut x = 0;
    for package in packages.iter() {
        if package.icon.has_data() {
            let y = window.height() as isize - package.icon.height() as isize;

            if mouse_y >= y as i32 && mouse_x >= x && mouse_x < x + package.icon.width() as i32 {
                window.rect(x as i32, y as i32,
                                  package.icon.width() as u32, package.icon.height() as u32,
                                  Color::rgba(128, 128, 128, 128));

                window.rect(x as i32, y as i32 - 16,
                    package.name.len() as u32 * 8, 16,
                    Color::rgba(0, 0, 0, 128));

                let mut c_x = x;
                for c in package.name.chars() {
                    window.char(c_x as i32, y as i32 - 16, c, Color::rgb(255, 255, 255));
                    c_x += 8;
                }
            }

            window.image(x as i32, y as i32,
                        package.icon.width() as u32,
                        package.icon.height() as u32,
                        &package.icon);
            x = x + package.icon.width() as i32;
        }
    }

    if shutdown.has_data() {
        x = window.width() as i32 - shutdown.width() as i32;
        let y = window.height() as isize - shutdown.height() as isize;

        if mouse_y >= y as i32 && mouse_x >= x &&
           mouse_x < x + shutdown.width() as i32 {
            window.rect(x as i32, y as i32,
                              shutdown.width() as u32, shutdown.height() as u32,
                              Color::rgba(128, 128, 128, 128));
        }

        window.image(x as i32, y as i32,
                        shutdown.width() as u32, shutdown.height() as u32,
                        &shutdown);
        x = x + shutdown.width() as i32;
    }

    window.sync();
}

fn draw_chooser(window: &mut Window, packages: &Vec<Package>, mouse_x: i32, mouse_y: i32){
    let w = window.width();

    window.set(Color::rgb(255, 255, 255));

    let mut y = 0;
    for package in packages.iter() {
        if mouse_y >= y as i32 && mouse_y < y + 32 {
            window.rect(0, y, w, 32, Color::rgb(128, 128, 128));
        }

        if package.icon.has_data() {
            window.image(0, y, package.icon.width() as u32, package.icon.height() as u32, &package.icon);
        }

        let mut c_x = 40;
        for c in package.name.chars() {
            window.char(c_x as i32, y + 8, c, Color::rgb(0, 0, 0));
            c_x += 8;
        }

        y += 32;
    }

    window.sync();
}

fn main() {
    let paths = env::args().skip(1);
    if paths.len() > 0 {
        for ref path in paths {
            let mut packages = get_packages();

            packages.retain(|package| -> bool {
                for accept in package.accepts.iter() {
                    if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                       (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                        return true;
                    }
                }
                false
            });

            if packages.len() > 1 {
                for package in packages.iter() {
                    println!("{:?}: {}", package.binary, package.icon.has_data());
                }

                let mut window = Window::new(-1, -1, 400, packages.len() as u32 * 32, path).unwrap();

                draw_chooser(&mut window, &packages, -1, -1);
                'choosing: loop {
                    for event in window.events() {
                        match event.to_option() {
                            EventOption::Mouse(mouse_event) => {
                                draw_chooser(&mut window, &packages, mouse_event.x, mouse_event.y);

                                if mouse_event.left_button {
                                    let mut y = 0;
                                    for package in packages.iter() {
                                        if package.icon.has_data() {
                                            if mouse_event.y >= y && mouse_event.y < y + 32 {
                                                if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                                    println!("{}: Failed to launch: {}", package.binary, err);
                                                }
                                                break 'choosing;
                                            }
                                            y += 32;
                                        }
                                    }
                                }
                            },
                            EventOption::Quit(_) => break 'choosing,
                            _ => ()
                        }
                    }

                    thread::yield_now();
                }
            } else if let Some(package) = packages.get(0) {
                if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                    println!("launcher: failed to launch '{}': {}", package.binary, err);
                }
            } else {
                println!("launcher: no application found for '{}'", path);
            }
        }
    } else {
        let packages = get_packages();

        let shutdown = BmpFile::from_path("/ui/actions/system-shutdown.bmp");
        if ! shutdown.has_data() {
            println!("launcher: failed to read shutdown icon");
        }

        let mut window = Window::new(0, 768 - 48, 1024, 48, "").unwrap();

        draw(&mut window, &packages, &shutdown, -1, -1);
        'running: loop {
            for event in window.events() {
                match event.to_option() {
                    EventOption::Mouse(mouse_event) => {
                        draw(&mut window, &packages, &shutdown, mouse_event.x, mouse_event.y);

                        if mouse_event.left_button {
                            let mut x = 0;
                            for package in packages.iter() {
                                if package.icon.has_data() {
                                    let y = window.height() as i32 - package.icon.height() as i32;
                                    if mouse_event.y >= y && mouse_event.x >= x &&
                                       mouse_event.x < x + package.icon.width() as i32 {
                                        if let Err(err) = Command::new(&package.binary).spawn() {
                                            println!("{}: Failed to launch: {}", package.binary, err);
                                        }
                                    }
                                    x = x + package.icon.width() as i32;
                                }
                            }

                            if shutdown.has_data() {
                                x = window.width() as i32 - shutdown.width() as i32;
                                let y = window.height() as i32 - shutdown.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + shutdown.width() as i32 {
                                       File::create("acpi:off");
                                       break 'running;
                                }
                            }
                        }
                    },
                    EventOption::Quit(_) => break 'running,
                    _ => ()
                }
            }

            thread::yield_now();
        }
    }
}
<commit_msg>Simpler launcher, much more respondent<commit_after>extern crate orbclient;

use std::env;
use std::fs::{self, File};
use std::process::Command;
use std::thread;

use orbclient::{BmpFile, Color, EventOption, Window};

use package::Package;

pub mod package;

fn get_packages() -> Vec<Package> {
    let mut packages: Vec<Package> = Vec::new();

    for entry_result in fs::read_dir("/apps/").unwrap() {
        let entry = entry_result.unwrap();
        if entry.file_type().unwrap().is_dir() {
            packages.push(Package::from_path(&("/apps/".to_string() + entry.file_name().to_str().unwrap())));
        }
    }

    packages
}

fn draw(window: &mut Window, packages: &Vec<Package>, shutdown: &BmpFile, selected: i32){
    let w = window.width();
    let h = window.height();
    window.set(Color::rgb(128, 128, 128));

    let mut x = 0;
    let mut i = 0;
    for package in packages.iter() {
        if package.icon.has_data() {
            let y = h as isize - package.icon.height() as isize;

            if i == selected {
                window.rect(x as i32, y as i32,
                                  package.icon.width() as u32, package.icon.height() as u32,
                                  Color::rgb(224, 224, 224));
            }

            window.image(x as i32, y as i32,
                        package.icon.width() as u32,
                        package.icon.height() as u32,
                        &package.icon);
            x = x + package.icon.width() as i32;
            i += 1;
        }
    }

    if shutdown.has_data() {
        x = w as i32 - shutdown.width() as i32;
        let y = h as isize - shutdown.height() as isize;

        if i == selected {
            window.rect(x as i32, y as i32,
                              shutdown.width() as u32, shutdown.height() as u32,
                              Color::rgb(224, 224, 224));
        }

        window.image(x as i32, y as i32,
                        shutdown.width() as u32, shutdown.height() as u32,
                        &shutdown);
        x = x + shutdown.width() as i32;
        i += 1;
    }

    window.sync();
}

fn draw_chooser(window: &mut Window, packages: &Vec<Package>, mouse_x: i32, mouse_y: i32){
    let w = window.width();

    window.set(Color::rgb(255, 255, 255));

    let mut y = 0;
    for package in packages.iter() {
        if mouse_y >= y as i32 && mouse_y < y + 32 {
            window.rect(0, y, w, 32, Color::rgb(128, 128, 128));
        }

        if package.icon.has_data() {
            window.image(0, y, package.icon.width() as u32, package.icon.height() as u32, &package.icon);
        }

        let mut c_x = 40;
        for c in package.name.chars() {
            window.char(c_x as i32, y + 8, c, Color::rgb(0, 0, 0));
            c_x += 8;
        }

        y += 32;
    }

    window.sync();
}

fn main() {
    let paths = env::args().skip(1);
    if paths.len() > 0 {
        for ref path in paths {
            let mut packages = get_packages();

            packages.retain(|package| -> bool {
                for accept in package.accepts.iter() {
                    if (accept.starts_with('*') && path.ends_with(&accept[1 ..])) ||
                       (accept.ends_with('*') && path.starts_with(&accept[.. accept.len() - 1])) {
                        return true;
                    }
                }
                false
            });

            if packages.len() > 1 {
                for package in packages.iter() {
                    println!("{:?}: {}", package.binary, package.icon.has_data());
                }

                let mut window = Window::new(-1, -1, 400, packages.len() as u32 * 32, path).unwrap();

                draw_chooser(&mut window, &packages, -1, -1);
                'choosing: loop {
                    for event in window.events() {
                        match event.to_option() {
                            EventOption::Mouse(mouse_event) => {
                                draw_chooser(&mut window, &packages, mouse_event.x, mouse_event.y);

                                if mouse_event.left_button {
                                    let mut y = 0;
                                    for package in packages.iter() {
                                        if package.icon.has_data() {
                                            if mouse_event.y >= y && mouse_event.y < y + 32 {
                                                if let Err(err) = Command::new(&package.binary).arg(path).spawn() {
                                                    println!("{}: Failed to launch: {}", package.binary, err);
                                                }
                                                break 'choosing;
                                            }
                                            y += 32;
                                        }
                                    }
                                }
                            },
                            EventOption::Quit(_) => break 'choosing,
                            _ => ()
                        }
                    }

                    thread::yield_now();
                }
            } else if let Some(package) = packages.get(0) {
                if let Err(err) = Command::new(&package.binary).arg(&path).spawn() {
                    println!("launcher: failed to launch '{}': {}", package.binary, err);
                }
            } else {
                println!("launcher: no application found for '{}'", path);
            }
        }
    } else {
        let packages = get_packages();

        let shutdown = BmpFile::from_path("/ui/actions/system-shutdown.bmp");
        if ! shutdown.has_data() {
            println!("launcher: failed to read shutdown icon");
        }

        let mut window = Window::new(0, 768 - 32, 1024, 32, "").unwrap();

        let mut selected = -1;

        draw(&mut window, &packages, &shutdown, selected);
        'running: loop {
            for event in window.events() {
                match event.to_option() {
                    EventOption::Mouse(mouse_event) => {
                        let mut now_selected = -1;

                        {
                            let mut x = 0;
                            let mut i = 0;
                            for package in packages.iter() {
                                if package.icon.has_data() {
                                    let y = window.height() as i32 - package.icon.height() as i32;
                                    if mouse_event.y >= y && mouse_event.x >= x &&
                                       mouse_event.x < x + package.icon.width() as i32 {
                                        now_selected = i;
                                    }
                                    x = x + package.icon.width() as i32;
                                    i += 1;
                                }
                            }

                            if shutdown.has_data() {
                                x = window.width() as i32 - shutdown.width() as i32;
                                let y = window.height() as i32 - shutdown.height() as i32;
                                if mouse_event.y >= y && mouse_event.x >= x &&
                                   mouse_event.x < x + shutdown.width() as i32 {
                                       now_selected = i;
                                }
                                i += 1;
                            }
                        }

                        if now_selected != selected {
                            selected = now_selected;
                            draw(&mut window, &packages, &shutdown, selected);
                        }

                        if mouse_event.left_button {
                            let mut i = 0;
                            for package in packages.iter() {
                                if package.icon.has_data() {
                                    if i == selected {
                                        if let Err(err) = Command::new(&package.binary).spawn() {
                                            println!("{}: Failed to launch: {}", package.binary, err);
                                        }
                                    }
                                    i += 1;
                                }
                            }

                            if shutdown.has_data() {
                                if i == selected {
                                       File::create("acpi:off");
                                }
                                i += 1;
                            }
                        }
                    },
                    EventOption::Quit(_) => break 'running,
                    _ => ()
                }
            }

            thread::yield_now();
        }
    }
}
<|endoftext|>


<commit_before>#![macro_escape]
macro_rules! hash_module (($hash_name:ident, $hashbytes:expr, $blockbytes:expr) => (

#[link(name = "sodium")]
extern {
    fn $hash_name(h: *mut u8,
                  m: *u8,
                  mlen: c_ulonglong) -> c_int;
}

pub static HASHBYTES: uint = $hashbytes;
pub static BLOCKBYTES: uint = $blockbytes;

/**
 * Digest-structure
 */
pub struct Digest(pub [u8, ..HASHBYTES]);

/**
 * `hash` hashes a message `m`. It returns a hash `h`.
 */
pub fn hash(m: &[u8]) -> Digest {
    unsafe {
        let mut h = [0, ..HASHBYTES];
        $hash_name(h.as_mut_ptr(), m.as_ptr(), m.len() as c_ulonglong);
        Digest(h)
    }
}

))
<commit_msg>Revert "The Digest buffer should public (fields are private by default now)"<commit_after>#![macro_escape]
macro_rules! hash_module (($hash_name:ident, $hashbytes:expr, $blockbytes:expr) => (

#[link(name = "sodium")]
extern {
    fn $hash_name(h: *mut u8,
                  m: *u8,
                  mlen: c_ulonglong) -> c_int;
}

pub static HASHBYTES: uint = $hashbytes;
pub static BLOCKBYTES: uint = $blockbytes;

/**
 * Digest-structure
 */
pub struct Digest([u8, ..HASHBYTES]);

/**
 * `hash` hashes a message `m`. It returns a hash `h`.
 */
pub fn hash(m: &[u8]) -> Digest {
    unsafe {
        let mut h = [0, ..HASHBYTES];
        $hash_name(h.as_mut_ptr(), m.as_ptr(), m.len() as c_ulonglong);
        Digest(h)
    }
}

))
<|endoftext|>