<commit_before>use prelude::{define_built_in, apply_program};

use math::rich_number::RichNumber;

use eval::context_item::ContextItem;
use eval::eval_context::{EvalContext, EvalError};
use eval::built_in_function::BuiltInFunction;

const UNARY_PARAMS_LENGTH: usize = 1;
const BINARY_PARAMS_LENGTH: usize = 2;

pub fn define_math(context: &mut EvalContext) {
    context.set_boolean("true", true);
    context.set_boolean("false", false);

    define_numeric_binary_op(context, "pow", |a, b| a.pow(b), |_, _| false);
    define_numeric_binary_op(context, "mul", |a, b| a * b, |_, _| false);

    define_numeric_binary_op(context, "add", |a, b| a + b, |_, _| true);
    define_numeric_binary_op(context, "sub", |a, b| a - b, |_, _| true);
    define_numeric_binary_op(context, "div", |a, b| a / b,
        |a, b| a.has_units() && b.has_units());
    define_numeric_binary_op(context, "mod", |a, b| a % b,
        |a, b| a.has_units() && b.has_units());

    define_boolean_binary_op(context, "eq", |a, b| a == b, true);
    define_boolean_binary_op(context, "ne", |a, b| a != b, true);

    define_boolean_binary_op(context, "gt",
        |a, b| a.unwrap_number() > b.unwrap_number(), true);
    define_boolean_binary_op(context, "ge",
        |a, b| a.unwrap_number() >= b.unwrap_number(), true);
    define_boolean_binary_op(context, "lt",
        |a, b| a.unwrap_number() < b.unwrap_number(), true);
    define_boolean_binary_op(context, "le",
        |a, b| a.unwrap_number() <= b.unwrap_number(), true);

    define_unary_operators(context);

    apply_program(context, include_str!("math.lion"));

    //TODO: sin, cos, tan, etc.
}

fn define_unary_operators(context: &mut EvalContext) {
    define_built_in(context, "not", UNARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |_, mut params| {
            if params.len() != UNARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(UNARY_PARAMS_LENGTH));
            }

            if !params[0].is_boolean() {
                return Err(EvalError::InvalidParam("Can only boolean negate a boolean value".to_owned()));
            }

            let arg = params.pop().unwrap().unwrap_boolean();
            debug_assert!(params.is_empty(), "Not all parameters used");

            Ok(ContextItem::Boolean(!arg))
        })
    );

    define_built_in(context, "neg", UNARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |_, mut params| {
            if params.len() != UNARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(UNARY_PARAMS_LENGTH));
            }

            if !params[0].is_number() {
                return Err(EvalError::InvalidParam("Can only negate a numeric value".to_owned()));
            }

            let arg = params.pop().unwrap().unwrap_number();
            debug_assert!(params.is_empty(), "Not all parameters used");

            Ok(ContextItem::Number(-arg))
        })
    );
}

/// Defines an operator that can take either booleans or numbers
/// Optionally coerces numbers into the same unit
/// Ensures that both arguments are numbers or both are booleans
fn define_boolean_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(ContextItem, ContextItem) -> bool {

    define_built_in(context, name, BINARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != BINARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(BINARY_PARAMS_LENGTH))
            }

            // params can be either number or boolean but not both
            if !((params[0].is_number() && params[1].is_number())
                || (params[0].is_boolean() && params[1].is_boolean())) {
                return Err(EvalError::InvalidParam("Expecting numeric or boolean argument, cannot compare numeric values to boolean values".to_owned()));
            }

            let mut rhs = params.remove(0);
            let lhs = params.remove(0);
            debug_assert!(params.is_empty(), "Not all parameters used");

            if coerce_first && lhs.is_number() && rhs.is_number() {
                // May possibly attempt to convert in the other
                // direction too in the future
                let lhs_unit = match lhs {
                    ContextItem::Number(RichNumber { ref unit, .. }) => unit,
                    _ => unreachable!(),
                };
                rhs = try!(context.convert(rhs.unwrap_number(), *lhs_unit));
            }

            Ok(ContextItem::Boolean(operator(lhs, rhs)))
        })
    );
}

/// Defines an operator that can only take numeric arguments
/// Optionally takes a function used to define whether the numeric arguments should be coerced into the same unit
fn define_numeric_binary_op<F: 'static, G: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: G)
    where F: Fn(RichNumber, RichNumber) -> RichNumber,
          G: Fn(RichNumber, RichNumber) -> bool {

    define_built_in(context, name, BINARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != BINARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(BINARY_PARAMS_LENGTH))
            }

            if !params[0].is_number() || !params[1].is_number() {
                return Err(EvalError::InvalidParam("Expecting numeric argument".to_owned()));
            }

            let mut rhs = params.pop().unwrap().unwrap_number();
            let lhs = params.pop().unwrap().unwrap_number();
            debug_assert!(params.is_empty(), "Not all parameters used");

            if coerce_first(lhs, rhs) {
                // May possibly attempt to convert in the other
                // direction too in the future
                rhs = try!(context.convert(rhs, lhs.unit)).unwrap_number();
            }

            Ok(ContextItem::Number(operator(lhs, rhs)))
        })
    );
}

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

    use eval::context_item::ContextItem;
    use eval::eval_context::EvalContext;
    use math::rich_number::RichNumber;

    #[test]
    fn math_operators() {
        let mut context = EvalContext::prelude();

        // add 1 (sub (mul 2 (pow 4 3)) (div (mod 7 3) 10))
        // 1 + ((2 * (4 ** 3)) - ((7 % 3) / 10))
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("add".to_owned())),
            ExprItem::SingleTerm(Term::Number(1f64)),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Symbol("sub".to_owned())),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("mul".to_owned())),
                    ExprItem::SingleTerm(Term::Number(2f64)),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("pow".to_owned())),
                        ExprItem::SingleTerm(Term::Number(4f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                ]),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("div".to_owned())),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("mod".to_owned())),
                        ExprItem::SingleTerm(Term::Number(7f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                    ExprItem::SingleTerm(Term::Number(10f64)),
                ]),
            ]),
        ])).unwrap().unwrap_number(), RichNumber::from(128.9f64));
    }

    #[test]
    fn division_with_or_without_units() {
        let mut context = EvalContext::prelude();

        let fake_unit = "foounit";
        context.call("defineUnit", vec![
            ContextItem::Constant(fake_unit.to_owned()),
        ]).unwrap().unwrap();

        let unit = context.lookup_unit_name(fake_unit).unwrap();

        // 3 foounit / 2 should equal 1.5 foounit
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
        ])).unwrap().unwrap_number(), RichNumber::from_unit(1.5f64, unit));

        // 3 foounit / 2 foounit should equal 1.5
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
        ])).unwrap().unwrap_number(), RichNumber::from(1.5f64));
    }

    #[test]
    fn can_compare_booleans_to_booleans() {
        let mut context = EvalContext::prelude();

        // true == false should be false and should not error
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("true".to_owned())),
            ExprItem::SingleTerm(Term::Symbol("==".to_owned())),
            ExprItem::SingleTerm(Term::Symbol("false".to_owned())),
        ])).unwrap().unwrap_boolean(), false);
    }

    #[test]
    fn can_compare_numbers_to_numbers() {
        let mut context = EvalContext::prelude();

        // 2 == 2 should be true and should not error
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(2f64)),
            ExprItem::SingleTerm(Term::Symbol("==".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
        ])).unwrap().unwrap_boolean(), true);
    }
}

<commit_msg>Testing neg and not<commit_after>use prelude::{define_built_in, apply_program};

use math::rich_number::RichNumber;

use eval::context_item::ContextItem;
use eval::eval_context::{EvalContext, EvalError};
use eval::built_in_function::BuiltInFunction;

const UNARY_PARAMS_LENGTH: usize = 1;
const BINARY_PARAMS_LENGTH: usize = 2;

pub fn define_math(context: &mut EvalContext) {
    context.set_boolean("true", true);
    context.set_boolean("false", false);

    define_numeric_binary_op(context, "pow", |a, b| a.pow(b), |_, _| false);
    define_numeric_binary_op(context, "mul", |a, b| a * b, |_, _| false);

    define_numeric_binary_op(context, "add", |a, b| a + b, |_, _| true);
    define_numeric_binary_op(context, "sub", |a, b| a - b, |_, _| true);
    define_numeric_binary_op(context, "div", |a, b| a / b,
        |a, b| a.has_units() && b.has_units());
    define_numeric_binary_op(context, "mod", |a, b| a % b,
        |a, b| a.has_units() && b.has_units());

    define_boolean_binary_op(context, "eq", |a, b| a == b, true);
    define_boolean_binary_op(context, "ne", |a, b| a != b, true);

    define_boolean_binary_op(context, "gt",
        |a, b| a.unwrap_number() > b.unwrap_number(), true);
    define_boolean_binary_op(context, "ge",
        |a, b| a.unwrap_number() >= b.unwrap_number(), true);
    define_boolean_binary_op(context, "lt",
        |a, b| a.unwrap_number() < b.unwrap_number(), true);
    define_boolean_binary_op(context, "le",
        |a, b| a.unwrap_number() <= b.unwrap_number(), true);

    define_unary_operators(context);

    apply_program(context, include_str!("math.lion"));

    //TODO: sin, cos, tan, etc.
}

fn define_unary_operators(context: &mut EvalContext) {
    define_built_in(context, "not", UNARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |_, mut params| {
            if params.len() != UNARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(UNARY_PARAMS_LENGTH));
            }

            if !params[0].is_boolean() {
                return Err(EvalError::InvalidParam("Can only boolean negate a boolean value".to_owned()));
            }

            let arg = params.pop().unwrap().unwrap_boolean();
            debug_assert!(params.is_empty(), "Not all parameters used");

            Ok(ContextItem::Boolean(!arg))
        })
    );

    define_built_in(context, "neg", UNARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |_, mut params| {
            if params.len() != UNARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(UNARY_PARAMS_LENGTH));
            }

            if !params[0].is_number() {
                return Err(EvalError::InvalidParam("Can only negate a numeric value".to_owned()));
            }

            let arg = params.pop().unwrap().unwrap_number();
            debug_assert!(params.is_empty(), "Not all parameters used");

            Ok(ContextItem::Number(-arg))
        })
    );
}

/// Defines an operator that can take either booleans or numbers
/// Optionally coerces numbers into the same unit
/// Ensures that both arguments are numbers or both are booleans
fn define_boolean_binary_op<F: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: bool)
    where F: Fn(ContextItem, ContextItem) -> bool {

    define_built_in(context, name, BINARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != BINARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(BINARY_PARAMS_LENGTH))
            }

            // params can be either number or boolean but not both
            if !((params[0].is_number() && params[1].is_number())
                || (params[0].is_boolean() && params[1].is_boolean())) {
                return Err(EvalError::InvalidParam("Expecting numeric or boolean argument, cannot compare numeric values to boolean values".to_owned()));
            }

            let mut rhs = params.remove(0);
            let lhs = params.remove(0);
            debug_assert!(params.is_empty(), "Not all parameters used");

            if coerce_first && lhs.is_number() && rhs.is_number() {
                // May possibly attempt to convert in the other
                // direction too in the future
                let lhs_unit = match lhs {
                    ContextItem::Number(RichNumber { ref unit, .. }) => unit,
                    _ => unreachable!(),
                };
                rhs = try!(context.convert(rhs.unwrap_number(), *lhs_unit));
            }

            Ok(ContextItem::Boolean(operator(lhs, rhs)))
        })
    );
}

/// Defines an operator that can only take numeric arguments
/// Optionally takes a function used to define whether the numeric arguments should be coerced into the same unit
fn define_numeric_binary_op<F: 'static, G: 'static>(context: &mut EvalContext, name: &str, operator: F, coerce_first: G)
    where F: Fn(RichNumber, RichNumber) -> RichNumber,
          G: Fn(RichNumber, RichNumber) -> bool {

    define_built_in(context, name, BINARY_PARAMS_LENGTH,
        BuiltInFunction::new(move |context, mut params| {
            if params.len() != BINARY_PARAMS_LENGTH {
                return Err(EvalError::ExpectedParams(BINARY_PARAMS_LENGTH))
            }

            if !params[0].is_number() || !params[1].is_number() {
                return Err(EvalError::InvalidParam("Expecting numeric argument".to_owned()));
            }

            let mut rhs = params.pop().unwrap().unwrap_number();
            let lhs = params.pop().unwrap().unwrap_number();
            debug_assert!(params.is_empty(), "Not all parameters used");

            if coerce_first(lhs, rhs) {
                // May possibly attempt to convert in the other
                // direction too in the future
                rhs = try!(context.convert(rhs, lhs.unit)).unwrap_number();
            }

            Ok(ContextItem::Number(operator(lhs, rhs)))
        })
    );
}

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

    use eval::context_item::ContextItem;
    use eval::eval_context::EvalContext;
    use math::rich_number::{RichNumber, Unit};

    #[test]
    fn math_operators() {
        let mut context = EvalContext::prelude();

        // add 1 (sub (mul 2 (pow 4 3)) (div (mod 7 3) 10))
        // 1 + ((2 * (4 ** 3)) - ((7 % 3) / 10))
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("add".to_owned())),
            ExprItem::SingleTerm(Term::Number(1f64)),
            ExprItem::Group(vec![
                ExprItem::SingleTerm(Term::Symbol("sub".to_owned())),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("mul".to_owned())),
                    ExprItem::SingleTerm(Term::Number(2f64)),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("pow".to_owned())),
                        ExprItem::SingleTerm(Term::Number(4f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                ]),
                ExprItem::Group(vec![
                    ExprItem::SingleTerm(Term::Symbol("div".to_owned())),
                    ExprItem::Group(vec![
                        ExprItem::SingleTerm(Term::Symbol("mod".to_owned())),
                        ExprItem::SingleTerm(Term::Number(7f64)),
                        ExprItem::SingleTerm(Term::Number(3f64)),
                    ]),
                    ExprItem::SingleTerm(Term::Number(10f64)),
                ]),
            ]),
        ])).unwrap().unwrap_number(), RichNumber::from(128.9f64));
    }

    #[test]
    fn division_with_or_without_units() {
        let mut context = EvalContext::prelude();

        let fake_unit = "foounit";
        assert_eq!(context.call("defineUnit", vec![
            ContextItem::Constant(fake_unit.to_owned()),
        ]).unwrap().unwrap(), ContextItem::Nothing);

        let unit = context.lookup_unit_name(fake_unit).unwrap();

        // 3 foounit / 2 should equal 1.5 foounit
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
        ])).unwrap().unwrap_number(), RichNumber::from_unit(1.5f64, unit));

        // 3 foounit / 2 foounit should equal 1.5
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(3f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
            ExprItem::SingleTerm(Term::Symbol("/".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
            ExprItem::SingleTerm(Term::Symbol(fake_unit.to_owned())),
        ])).unwrap().unwrap_number(), RichNumber::from(1.5f64));
    }

    #[test]
    fn can_compare_booleans_to_booleans() {
        let mut context = EvalContext::prelude();

        // true == false should be false and should not error
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Symbol("true".to_owned())),
            ExprItem::SingleTerm(Term::Symbol("==".to_owned())),
            ExprItem::SingleTerm(Term::Symbol("false".to_owned())),
        ])).unwrap().unwrap_boolean(), false);
    }

    #[test]
    fn can_compare_numbers_to_numbers() {
        let mut context = EvalContext::prelude();

        // 2 == 2 should be true and should not error
        assert_eq!(context.apply(Statement::Expression(vec![
            ExprItem::SingleTerm(Term::Number(2f64)),
            ExprItem::SingleTerm(Term::Symbol("==".to_owned())),
            ExprItem::SingleTerm(Term::Number(2f64)),
        ])).unwrap().unwrap_boolean(), true);
    }

    #[test]
    fn can_negate_numbers() {
        let mut context = EvalContext::prelude();

        let fake_unit: Unit = 2;
        let number: f64 = 34.314f64;

        assert_eq!(
            context.call("neg", vec![
                ContextItem::Number(RichNumber::from_unit(number, fake_unit)),
            ]).unwrap().unwrap().unwrap_number(),
            RichNumber::from_unit(-number, fake_unit)
        );
    }

    #[test]
    fn can_negate_booleans() {
        let mut context = EvalContext::prelude();

        assert_eq!(context.call("not", vec![
            ContextItem::Boolean(true),
        ]).unwrap().unwrap().unwrap_boolean(), false);
    }
}

<|endoftext|>


<commit_before>// Here lists all the paths that will be used by wiz
use std::path::{ Path, PathBuf };

///
/// Appends `append` to `path`.
///
pub trait PathAppend {
    fn append(&mut self, input: &str) -> &Path;
}

impl PathAppend for PathBuf {
    fn append(&mut self, input: &str) -> &Path {
        self.push(input);
        self.as_path()
    }
}

pub const DOWNLOAD_DIR: &'static str = "downloaded/";
pub const EXTRACT_DIR: &'static str = "extracted/";
pub const PACKAGE_INSTALL_DIR: &'static str = "installed_package/";
pub const PACKAGE_LIST_DIR: &'static str = "package_lists/";<commit_msg>Return PathBuf instead of &Path<commit_after>// Here lists all the paths that will be used by wiz
use std::path::{ Path, PathBuf };

///
/// Appends `append` to `path`.
///
pub trait PathAppend {
    fn append(&self, input: &str) -> PathBuf;
}

impl PathAppend for PathBuf {
    fn append(&self, input: &str) -> PathBuf {
        let mut buf = self.to_path_buf().clone();
        buf.push(input);
        buf.clone()
    }
}

pub const DOWNLOAD_DIR: &'static str = "downloaded/";
pub const EXTRACT_DIR: &'static str = "extracted/";
pub const PACKAGE_INSTALL_DIR: &'static str = "installed_package/";
pub const PACKAGE_LIST_DIR: &'static str = "package_lists/";<|endoftext|>


<commit_before>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

use std::mem;
use std::os::unix::io::{RawFd, AsRawFd};
use std::io::{Read, Write, Error, ErrorKind};

use frame;

use super::{Blocking, NonBlocking};

const BUF_SIZE: usize = 1024;

#[derive(Clone)]
pub struct Plain<T: Read + Write> {
    inner: T,
    rx_buf: Vec<u8>,
    tx_buf: Vec<u8>
}

impl<T: Read + Write> Plain<T> {
    pub fn new(stream: T) -> Plain<T> {
        Plain {
            inner: stream,
            rx_buf: Vec::<u8>::with_capacity(BUF_SIZE),
            tx_buf: Vec::<u8>::with_capacity(BUF_SIZE)
        }
    }
}

impl<T: Read + Write> Blocking for Plain<T> {
    fn b_recv(&mut self) -> Result<Vec<u8>, Error> {
        loop {
            let mut buf = [0u8; BUF_SIZE];
            let read_result = self.inner.read(&mut buf);
            if read_result.is_err() {
                let err = read_result.unwrap_err();
                return Err(err);
            }

            let num_read = read_result.unwrap();
            self.rx_buf.extend_from_slice(&buf[0..num_read]);

            match frame::from_raw_parts(&mut self.rx_buf) {
                Some(frame) => {
                    return Ok(frame);
                }
                None => { }
            };
        }
    }

    fn b_send(&mut self, buf: &[u8]) -> Result<(), Error> {
        let frame = frame::new(buf);
        let write_result = self.inner.write(&frame[..]);
        if write_result.is_err() {
            let err = write_result.unwrap_err();
            return Err(err);
        }

        Ok(())
    }
}

impl<T: Read + Write> NonBlocking for Plain<T> {
    fn nb_recv(&mut self) -> Result<Vec<Vec<u8>>, Error> {
        loop {
            let mut buf = [0u8; BUF_SIZE];
            let read_result = self.inner.read(&mut buf);
            if read_result.is_err() {
                let err = read_result.unwrap_err();
                if err.kind() == ErrorKind::WouldBlock {
                    trace!("Received WouldBlock");
                    break;
                }
                return Err(err);
            }

            let num_read = read_result.unwrap();
            self.rx_buf.extend_from_slice(&buf[0..num_read]);
        }

        let mut ret_buf = Vec::<Vec<u8>>::with_capacity(5);
        while let Some(frame) = frame::from_raw_parts(&mut self.rx_buf) {
            ret_buf.push(frame);
        }

        if ret_buf.len() > 0 {
            return Ok(ret_buf);
        }

        Err(Error::new(ErrorKind::WouldBlock, "WouldBlock"))
    }

    fn nb_send(&mut self, buf: &[u8]) -> Result<(), Error> {
        let frame = frame::new(buf);
        self.tx_buf.extend_from_slice(&frame[..]);

        let write_result = self.inner.write(&self.tx_buf[..]);
        if write_result.is_err() {
            let err = write_result.unwrap_err();
            return Err(err);
        }

        let num_written = write_result.unwrap();
        if num_written == 0 {
            return Err(Error::new(ErrorKind::Other, "Write returned zero"));
        }

        if num_written < self.tx_buf.len() {
            let tx_buf_len = self.tx_buf.len();
            let remaining_len = self.tx_buf.len() - num_written;

            let mut buf = Vec::<u8>::with_capacity(remaining_len);
            buf.extend_from_slice(&self.tx_buf[num_written..tx_buf_len]);

            mem::swap(&mut buf, &mut self.tx_buf);

            return Err(Error::new(ErrorKind::WouldBlock, "WouldBlock"));
        }

        Ok(())
    }
}

impl<T: Read + Write + AsRawFd> AsRawFd for Plain<T> {
    fn as_raw_fd(&self) -> RawFd {
        self.inner.as_raw_fd()
    }
}
<commit_msg>Fixing non-blocking sends<commit_after>// Copyright 2015 Nathan Sizemore <nathanrsizemore@gmail.com>
//
// This Source Code Form is subject to the terms of the
// Mozilla Public License, v. 2.0. If a copy of the MPL was not
// distributed with this file, You can obtain one at
// http://mozilla.org/MPL/2.0/.

use std::mem;
use std::os::unix::io::{RawFd, AsRawFd};
use std::io::{Read, Write, Error, ErrorKind};

use frame;

use super::{Blocking, NonBlocking};

const BUF_SIZE: usize = 1024;

#[derive(Clone)]
pub struct Plain<T: Read + Write> {
    inner: T,
    rx_buf: Vec<u8>,
    tx_buf: Vec<u8>
}

impl<T: Read + Write> Plain<T> {
    pub fn new(stream: T) -> Plain<T> {
        Plain {
            inner: stream,
            rx_buf: Vec::<u8>::with_capacity(BUF_SIZE),
            tx_buf: Vec::<u8>::with_capacity(BUF_SIZE)
        }
    }
}

impl<T: Read + Write> Blocking for Plain<T> {
    fn b_recv(&mut self) -> Result<Vec<u8>, Error> {
        loop {
            let mut buf = [0u8; BUF_SIZE];
            let read_result = self.inner.read(&mut buf);
            if read_result.is_err() {
                let err = read_result.unwrap_err();
                return Err(err);
            }

            let num_read = read_result.unwrap();
            self.rx_buf.extend_from_slice(&buf[0..num_read]);

            match frame::from_raw_parts(&mut self.rx_buf) {
                Some(frame) => {
                    return Ok(frame);
                }
                None => { }
            };
        }
    }

    fn b_send(&mut self, buf: &[u8]) -> Result<(), Error> {
        let frame = frame::new(buf);
        let write_result = self.inner.write(&frame[..]);
        if write_result.is_err() {
            let err = write_result.unwrap_err();
            return Err(err);
        }

        Ok(())
    }
}

impl<T: Read + Write> NonBlocking for Plain<T> {
    fn nb_recv(&mut self) -> Result<Vec<Vec<u8>>, Error> {
        loop {
            let mut buf = [0u8; BUF_SIZE];
            let read_result = self.inner.read(&mut buf);
            if read_result.is_err() {
                let err = read_result.unwrap_err();
                if err.kind() == ErrorKind::WouldBlock {
                    trace!("Received WouldBlock");
                    break;
                }
                return Err(err);
            }

            let num_read = read_result.unwrap();
            self.rx_buf.extend_from_slice(&buf[0..num_read]);
        }

        let mut ret_buf = Vec::<Vec<u8>>::with_capacity(5);
        while let Some(frame) = frame::from_raw_parts(&mut self.rx_buf) {
            ret_buf.push(frame);
        }

        if ret_buf.len() > 0 {
            return Ok(ret_buf);
        }

        Err(Error::new(ErrorKind::WouldBlock, "WouldBlock"))
    }

    fn nb_send(&mut self, buf: &[u8]) -> Result<(), Error> {
        let frame = frame::new(buf);
        self.tx_buf.extend_from_slice(&frame[..]);

        let mut out_buf = Vec::<u8>::with_capacity(BUF_SIZE);
        mem::swap(&mut self.tx_buf, &mut out_buf);

        let write_result = self.inner.write(&out_buf[..]);
        if write_result.is_err() {
            let err = write_result.unwrap_err();
            return Err(err);
        }

        let num_written = write_result.unwrap();
        if num_written == 0 {
            return Err(Error::new(ErrorKind::Other, "Write returned zero"));
        }

        if num_written < out_buf.len() {
            let out_buf_len = out_buf.len();
            self.tx_buf.extend_from_slice(&out_buf[num_written..out_buf_len]);

            return Err(Error::new(ErrorKind::WouldBlock, "WouldBlock"));
        }

        Ok(())
    }
}

impl<T: Read + Write + AsRawFd> AsRawFd for Plain<T> {
    fn as_raw_fd(&self) -> RawFd {
        self.inner.as_raw_fd()
    }
}
<|endoftext|>


<commit_before>#![crate_name = "uu_pr"]

// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
//

#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
extern crate quick_error;
extern crate chrono;
extern crate getopts;
extern crate uucore;

use std::io::{BufRead, BufReader, stdin, stdout, stderr, Error, Read, Write, Stdout};
use std::vec::Vec;
use chrono::offset::Local;
use chrono::DateTime;
use getopts::{Matches, Options};
use std::fs::{metadata, File};
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
use quick_error::ResultExt;

static NAME: &str = "pr";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static LINES_PER_PAGE: usize = 66;
static HEADER_LINES_PER_PAGE: usize = 5;
static TRAILER_LINES_PER_PAGE: usize = 5;
static CONTENT_LINES_PER_PAGE: usize = LINES_PER_PAGE - HEADER_LINES_PER_PAGE - TRAILER_LINES_PER_PAGE;
static NUMBERING_MODE_DEFAULT_SEPARATOR: &str = "\t";
static NUMBERING_MODE_DEFAULT_WIDTH: usize = 5;
static STRING_HEADER_OPTION: &str = "h";
static NUMBERING_MODE_OPTION: &str = "n";
static FILE_STDIN: &str = "-";

struct OutputOptions {
    /// Line numbering mode
    number: Option<NumberingMode>,
    header: String,
    double_spaced: bool,
    line_separator: String,
    last_modified_time: String,
}

impl AsRef<OutputOptions> for OutputOptions {
    fn as_ref(&self) -> &OutputOptions {
        self
    }
}

struct NumberingMode {
    /// Line numbering mode
    width: usize,
    separator: String,
}

impl Default for NumberingMode {
    fn default() -> NumberingMode {
        NumberingMode {
            width: NUMBERING_MODE_DEFAULT_WIDTH,
            separator: NUMBERING_MODE_DEFAULT_SEPARATOR.to_string(),
        }
    }
}

enum InputType {
    Directory,
    File,
    StdIn,
    SymLink,
    #[cfg(unix)]
    BlockDevice,
    #[cfg(unix)]
    CharacterDevice,
    #[cfg(unix)]
    Fifo,
    #[cfg(unix)]
    Socket,
}

quick_error! {
    #[derive(Debug)]
    enum PrError {
        Input(err: Error, path: String) {
            context(path: &'a str, err: Error) -> (err, path.to_owned())
            display("pr: Reading from input {0} gave error", path)
            cause(err)
        }

        UnknownFiletype(path: String) {
            display("pr: {0}: unknown filetype", path)
        }

        EncounteredErrors(msg: String) {
            display("pr: {0} encountered", msg)
        }

        IsDirectory(path: String) {
            display("pr: {0}: Is a directory", path)
        }

        IsSocket(path: String) {
            display("pr: cannot open {}, Operation not supported on socket", path)
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optopt(
        STRING_HEADER_OPTION,
        "",
        "Use the string header to replace the file name \
     in the header line.",
        "STRING",
    );

    opts.optflag(
        "d",
        "",
        "Produce output that is double spaced. An extra <newline> character is output following every <newline>
           found in the input.",
    );

    opts.optflagopt(
        NUMBERING_MODE_OPTION,
        "",
        "Provide width digit line numbering.  The default for width, if not specified, is 5.  The number occupies
           the first width column positions of each text column or each line of -m output.  If char (any nondigit
           character) is given, it is appended to the line number to separate it from whatever follows.  The default
           for char is a <tab>.  Line numbers longer than width columns are truncated.",
        "[char][width]",
    );

    opts.optflag("", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => panic!("Invalid options\n{}", e),
    };

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let mut files: Vec<String> = matches.free.clone();
    if files.is_empty() {
        //For stdin
        files.push(FILE_STDIN.to_owned());
    }

    if matches.opt_present("help") {
        return print_usage(&mut opts, &matches);
    }

    for f in files {
        let header: &String = &matches.opt_str(STRING_HEADER_OPTION).unwrap_or(f.to_string());
        let options: &OutputOptions = &build_options(&matches, header, &f);
        let status: i32 = pr(&f, options);
        if status != 0 {
            return status;
        }
    }
    return 0;
}

fn print_usage(opts: &mut Options, matches: &Matches) -> i32 {
    println!("{} {} -- print files", NAME, VERSION);
    println!();
    println!("Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]]
        [-L locale] [-h header] [[-i] [char] [gap]]
        [-l lines] [-o offset] [[-s] [char]] [[-n] [char]
        [width]] [-w width] [-] [file ...].", NAME);
    println!();
    let usage: &str = "The pr utility is a printing and pagination filter
     for text files.  When multiple input files are spec-
     ified, each is read, formatted, and written to stan-
     dard output.  By default, the input is separated
     into 66-line pages, each with

     o   A 5-line header with the page number, date,
         time, and the pathname of the file.

     o   A 5-line trailer consisting of blank lines.

     If standard output is associated with a terminal,
     diagnostic messages are suppressed until the pr
     utility has completed processing.

     When multiple column output is specified, text col-
     umns are of equal width.  By default text columns
     are separated by at least one <blank>.  Input lines
     that do not fit into a text column are truncated.
     Lines are not truncated under single column output.";
    println!("{}", opts.usage(usage));
    if matches.free.is_empty() {
        return 1;
    }
    return 0;
}

fn build_options(matches: &Matches, header: &String, path: &String) -> OutputOptions {
    let numbering_options: Option<NumberingMode> = matches.opt_str(NUMBERING_MODE_OPTION).map(|i| {
        NumberingMode {
            width: i.parse::<usize>().unwrap_or(NumberingMode::default().width),
            separator: NumberingMode::default().separator,
        }
    }).or_else(|| {
        if matches.opt_present(NUMBERING_MODE_OPTION) {
            return Some(NumberingMode::default());
        }
        return None;
    });

    let line_separator: String = if matches.opt_present("d") {
        "\n\n".to_string()
    } else {
        "\n".to_string()
    };

    let last_modified_time = if path.eq(FILE_STDIN) {
        current_time()
    } else {
        file_last_modified_time(path)
    };

    OutputOptions {
        number: numbering_options,
        header: header.to_string(),
        double_spaced: matches.opt_present("d"),
        line_separator,
        last_modified_time,
    }
}

fn open(path: &str) -> Result<Box<Read>, PrError> {
    match get_input_type(path) {
        Some(InputType::Directory) => Err(PrError::IsDirectory(path.to_string())),
        #[cfg(unix)]
        Some(InputType::Socket) => {
            Err(PrError::IsSocket(path.to_string()))
        }
        Some(InputType::StdIn) => {
            let stdin = stdin();
            Ok(Box::new(stdin) as Box<Read>)
        }
        Some(_) => Ok(Box::new(File::open(path).context(path)?) as Box<Read>),
        None => Err(PrError::UnknownFiletype(path.to_string()))
    }
}

fn pr(path: &str, options: &OutputOptions) -> i32 {
    let mut i = 0;
    let mut page: usize = 0;
    let mut buffered_content: Vec<String> = Vec::new();
    match open(path) {
        Ok(reader) => {
            // TODO Replace the loop
            for line in BufReader::new(reader).lines() {
                if i == CONTENT_LINES_PER_PAGE {
                    page = page + 1;
                    i = 0;
                    prepare_page(&mut buffered_content, options, &page);
                }
                match line {
                    Ok(content) => buffered_content.push(content),
                    Err(error) => {
                        writeln!(&mut stderr(), "pr: Unable to read from input type {}\n{}", path, error.to_string());
                        return -1;
                    }
                }
                i = i + 1;
            }

            if i != 0 {
                page = page + 1;
                prepare_page(&mut buffered_content, options, &page);
            }
        }
        Err(error) => {
            writeln!(&mut stderr(), "{}", error);
            return -1;
        }
    }
    return 0;
}

fn print_page(header_content: &Vec<String>, lines: &Vec<String>, options: &OutputOptions, page: &usize) {
    let trailer_content: Vec<String> = trailer_content();
    assert_eq!(lines.len() <= CONTENT_LINES_PER_PAGE, true, "Only {} lines of content allowed in a pr output page", CONTENT_LINES_PER_PAGE);
    assert_eq!(header_content.len(), HEADER_LINES_PER_PAGE, "Only {} lines of content allowed in a pr header", HEADER_LINES_PER_PAGE);
    assert_eq!(trailer_content.len(), TRAILER_LINES_PER_PAGE, "Only {} lines of content allowed in a pr trailer", TRAILER_LINES_PER_PAGE);
    let out: &mut Stdout = &mut stdout();
    let line_separator = options.as_ref().line_separator.as_bytes();

    out.lock();
    for x in header_content {
        out.write(x.as_bytes());
        out.write(line_separator);
    }

    let width: usize = options.as_ref()
        .number.as_ref()
        .map(|i| i.width)
        .unwrap_or(0);
    let separator: String = options.as_ref()
        .number.as_ref()
        .map(|i| i.separator.to_string())
        .unwrap_or(NumberingMode::default().separator);

    let prev_lines = CONTENT_LINES_PER_PAGE * (page - 1);
    let mut i = 1;
    for x in lines {
        if options.number.is_none() {
            out.write(x.as_bytes());
        } else {
            let fmtd_line_number: String = get_fmtd_line_number(&width, prev_lines + i, &separator);
            out.write(format!("{}{}", fmtd_line_number, x).as_bytes());
        }
        out.write(line_separator);
        i = i + 1;
    }
    for x in trailer_content {
        out.write(x.as_bytes());
        out.write(line_separator);
    }
    out.flush();
}

fn get_fmtd_line_number(width: &usize, line_number: usize, separator: &String) -> String {
    let line_str = line_number.to_string();
    if line_str.len() >= *width {
        format!("{:>width$}{}", &line_str[line_str.len() - *width..], separator, width = width)
    } else {
        format!("{:>width$}{}", line_str, separator, width = width)
    }
}

fn prepare_page(buffered_content: &mut Vec<String>, options: &OutputOptions, page: &usize) {
    let header: Vec<String> = header_content(&options, &page);
    print_page(&header, buffered_content, &options, &page);
    buffered_content.clear();
}

fn header_content(options: &OutputOptions, page: &usize) -> Vec<String> {
    let first_line: String = format!("{} {} Page {}", options.last_modified_time, options.header, page);
    vec!["".to_string(), "".to_string(), first_line, "".to_string(), "".to_string()]
}

fn file_last_modified_time(path: &str) -> String {
    let file_metadata = metadata(path);
    return file_metadata.map(|i| {
        return i.modified().map(|x| {
            let datetime: DateTime<Local> = x.into();
            datetime.format("%b %d %H:%M %Y").to_string()
        }).unwrap_or(String::new());
    }).unwrap_or(String::new());
}

fn current_time() -> String {
    let datetime: DateTime<Local> = Local::now();
    datetime.format("%b %d %H:%M %Y").to_string()
}

fn trailer_content() -> Vec<String> {
    vec!["".to_string(), "".to_string(), "".to_string(), "".to_string(), "".to_string()]
}

fn get_input_type(path: &str) -> Option<InputType> {
    if path == FILE_STDIN {
        return Some(InputType::StdIn);
    }

    metadata(path).map(|i| {
        match i.file_type() {
            #[cfg(unix)]
            ft if ft.is_block_device() =>
                {
                    Some(InputType::BlockDevice)
                }
            #[cfg(unix)]
            ft if ft.is_char_device() =>
                {
                    Some(InputType::CharacterDevice)
                }
            #[cfg(unix)]
            ft if ft.is_fifo() =>
                {
                    Some(InputType::Fifo)
                }
            #[cfg(unix)]
            ft if ft.is_socket() =>
                {
                    Some(InputType::Socket)
                }
            ft if ft.is_dir() => Some(InputType::Directory),
            ft if ft.is_file() => Some(InputType::File),
            ft if ft.is_symlink() => Some(InputType::SymLink),
            _ => None
        }
    }).unwrap_or(None)
}
<commit_msg>pr: add read buffer size and fix unused Result types<commit_after>#![crate_name = "uu_pr"]

// This file is part of the uutils coreutils package.
//
// For the full copyright and license information, please view the LICENSE file
// that was distributed with this source code.
//

#[cfg(unix)]
extern crate unix_socket;
#[macro_use]
extern crate quick_error;
extern crate chrono;
extern crate getopts;
extern crate uucore;

use std::io::{BufRead, BufReader, stdin, stdout, stderr, Error, Read, Write, Stdout};
use std::vec::Vec;
use chrono::offset::Local;
use chrono::DateTime;
use getopts::{Matches, Options};
use std::fs::{metadata, File};
#[cfg(unix)]
use std::os::unix::fs::FileTypeExt;
use quick_error::ResultExt;
use std::convert::From;

static NAME: &str = "pr";
static VERSION: &str = env!("CARGO_PKG_VERSION");
static LINES_PER_PAGE: usize = 66;
static HEADER_LINES_PER_PAGE: usize = 5;
static TRAILER_LINES_PER_PAGE: usize = 5;
static CONTENT_LINES_PER_PAGE: usize = LINES_PER_PAGE - HEADER_LINES_PER_PAGE - TRAILER_LINES_PER_PAGE;
static NUMBERING_MODE_DEFAULT_SEPARATOR: &str = "\t";
static NUMBERING_MODE_DEFAULT_WIDTH: usize = 5;
static STRING_HEADER_OPTION: &str = "h";
static NUMBERING_MODE_OPTION: &str = "n";
static FILE_STDIN: &str = "-";
static READ_BUFFER_SIZE: usize = 1024 * 64;

struct OutputOptions {
    /// Line numbering mode
    number: Option<NumberingMode>,
    header: String,
    line_separator: String,
    last_modified_time: String,
}

impl AsRef<OutputOptions> for OutputOptions {
    fn as_ref(&self) -> &OutputOptions {
        self
    }
}

struct NumberingMode {
    /// Line numbering mode
    width: usize,
    separator: String,
}

impl Default for NumberingMode {
    fn default() -> NumberingMode {
        NumberingMode {
            width: NUMBERING_MODE_DEFAULT_WIDTH,
            separator: NUMBERING_MODE_DEFAULT_SEPARATOR.to_string(),
        }
    }
}

enum InputType {
    Directory,
    File,
    StdIn,
    SymLink,
    #[cfg(unix)]
    BlockDevice,
    #[cfg(unix)]
    CharacterDevice,
    #[cfg(unix)]
    Fifo,
    #[cfg(unix)]
    Socket,
}

impl From<Error> for PrError {
    fn from(err: Error) -> Self {
        PrError::EncounteredErrors(err.to_string())
    }
}

quick_error! {
    #[derive(Debug)]
    enum PrError {
        Input(err: Error, path: String) {
            context(path: &'a str, err: Error) -> (err, path.to_owned())
            display("pr: Reading from input {0} gave error", path)
            cause(err)
        }

        UnknownFiletype(path: String) {
            display("pr: {0}: unknown filetype", path)
        }

        EncounteredErrors(msg: String) {
            display("pr: {0} encountered", msg)
        }

        IsDirectory(path: String) {
            display("pr: {0}: Is a directory", path)
        }

        IsSocket(path: String) {
            display("pr: cannot open {}, Operation not supported on socket", path)
        }
    }
}

pub fn uumain(args: Vec<String>) -> i32 {
    let mut opts = getopts::Options::new();

    opts.optopt(
        STRING_HEADER_OPTION,
        "",
        "Use the string header to replace the file name \
     in the header line.",
        "STRING",
    );

    opts.optflag(
        "d",
        "",
        "Produce output that is double spaced. An extra <newline> character is output following every <newline>
           found in the input.",
    );

    opts.optflagopt(
        NUMBERING_MODE_OPTION,
        "",
        "Provide width digit line numbering.  The default for width, if not specified, is 5.  The number occupies
           the first width column positions of each text column or each line of -m output.  If char (any nondigit
           character) is given, it is appended to the line number to separate it from whatever follows.  The default
           for char is a <tab>.  Line numbers longer than width columns are truncated.",
        "[char][width]",
    );

    opts.optflag("", "help", "display this help and exit");
    opts.optflag("V", "version", "output version information and exit");

    let matches = match opts.parse(&args[1..]) {
        Ok(m) => m,
        Err(e) => panic!("Invalid options\n{}", e),
    };

    if matches.opt_present("version") {
        println!("{} {}", NAME, VERSION);
        return 0;
    }

    let mut files: Vec<String> = matches.free.clone();
    if files.is_empty() {
        //For stdin
        files.push(FILE_STDIN.to_owned());
    }

    if matches.opt_present("help") {
        return print_usage(&mut opts, &matches);
    }

    for f in files {
        let header: &String = &matches.opt_str(STRING_HEADER_OPTION).unwrap_or(f.to_string());
        let options: &OutputOptions = &build_options(&matches, header, &f);
        let status: i32 = match pr(&f, options) {
            Err(error) => {
                writeln!(&mut stderr(), "{}", error);
                -1
            },
            _ => 0
        };
        if status != 0 {
            return status;
        }
    }
    return 0;
}

fn print_usage(opts: &mut Options, matches: &Matches) -> i32 {
    println!("{} {} -- print files", NAME, VERSION);
    println!();
    println!("Usage: {} [+page] [-column] [-adFfmprt] [[-e] [char] [gap]]
        [-L locale] [-h header] [[-i] [char] [gap]]
        [-l lines] [-o offset] [[-s] [char]] [[-n] [char]
        [width]] [-w width] [-] [file ...].", NAME);
    println!();
    let usage: &str = "The pr utility is a printing and pagination filter
     for text files.  When multiple input files are spec-
     ified, each is read, formatted, and written to stan-
     dard output.  By default, the input is separated
     into 66-line pages, each with

     o   A 5-line header with the page number, date,
         time, and the pathname of the file.

     o   A 5-line trailer consisting of blank lines.

     If standard output is associated with a terminal,
     diagnostic messages are suppressed until the pr
     utility has completed processing.

     When multiple column output is specified, text col-
     umns are of equal width.  By default text columns
     are separated by at least one <blank>.  Input lines
     that do not fit into a text column are truncated.
     Lines are not truncated under single column output.";
    println!("{}", opts.usage(usage));
    if matches.free.is_empty() {
        return 1;
    }
    return 0;
}

fn build_options(matches: &Matches, header: &String, path: &String) -> OutputOptions {
    let numbering_options: Option<NumberingMode> = matches.opt_str(NUMBERING_MODE_OPTION).map(|i| {
        NumberingMode {
            width: i.parse::<usize>().unwrap_or(NumberingMode::default().width),
            separator: NumberingMode::default().separator,
        }
    }).or_else(|| {
        if matches.opt_present(NUMBERING_MODE_OPTION) {
            return Some(NumberingMode::default());
        }
        return None;
    });

    let line_separator: String = if matches.opt_present("d") {
        "\n\n".to_string()
    } else {
        "\n".to_string()
    };

    let last_modified_time = if path.eq(FILE_STDIN) {
        current_time()
    } else {
        file_last_modified_time(path)
    };

    OutputOptions {
        number: numbering_options,
        header: header.to_string(),
        line_separator,
        last_modified_time,
    }
}

fn open(path: &str) -> Result<Box<Read>, PrError> {
    match get_input_type(path) {
        Some(InputType::Directory) => Err(PrError::IsDirectory(path.to_string())),
        #[cfg(unix)]
        Some(InputType::Socket) => {
            Err(PrError::IsSocket(path.to_string()))
        }
        Some(InputType::StdIn) => {
            let stdin = stdin();
            Ok(Box::new(stdin) as Box<Read>)
        }
        Some(_) => Ok(Box::new(File::open(path).context(path)?) as Box<Read>),
        None => Err(PrError::UnknownFiletype(path.to_string()))
    }
}

fn pr(path: &str, options: &OutputOptions) -> Result<i32, PrError> {
    let mut i = 0;
    let mut page: usize = 0;
    let mut buffered_content: Vec<String> = Vec::new();

    for line in BufReader::with_capacity(READ_BUFFER_SIZE, open(path)?).lines() {
        if i == CONTENT_LINES_PER_PAGE {
            page = page + 1;
            i = 0;
            print_page(&buffered_content, options, &page)?;
            buffered_content = Vec::new();
        }
        buffered_content.push(line?);
        i = i + 1;
    }

    if i != 0 {
        page = page + 1;
        print_page(&buffered_content, options, &page)?;
    }
    return Ok(0);
}

fn print_page(lines: &Vec<String>, options: &OutputOptions, page: &usize) -> Result<usize, Error> {
    let header: Vec<String> = header_content(options, page);
    let trailer_content: Vec<String> = trailer_content();
    assert_eq!(lines.len() <= CONTENT_LINES_PER_PAGE, true, "Only {} lines of content allowed in a pr output page", CONTENT_LINES_PER_PAGE);
    assert_eq!(header.len(), HEADER_LINES_PER_PAGE, "Only {} lines of content allowed in a pr header", HEADER_LINES_PER_PAGE);
    assert_eq!(trailer_content.len(), TRAILER_LINES_PER_PAGE, "Only {} lines of content allowed in a pr trailer", TRAILER_LINES_PER_PAGE);
    let out: &mut Stdout = &mut stdout();
    let line_separator = options.as_ref().line_separator.as_bytes();
    let mut lines_written = 0;

    out.lock();
    for x in header {
        out.write(x.as_bytes())?;
        out.write(line_separator)?;
        lines_written += 1;
    }

    let width: usize = options.as_ref()
        .number.as_ref()
        .map(|i| i.width)
        .unwrap_or(0);
    let separator: String = options.as_ref()
        .number.as_ref()
        .map(|i| i.separator.to_string())
        .unwrap_or(NumberingMode::default().separator);

    let prev_lines = CONTENT_LINES_PER_PAGE * (page - 1);
    let mut i = 1;
    for x in lines {
        if options.number.is_none() {
            out.write(x.as_bytes())?;
        } else {
            let fmtd_line_number: String = get_fmtd_line_number(&width, prev_lines + i, &separator);
            out.write(format!("{}{}", fmtd_line_number, x).as_bytes())?;
        }
        out.write(line_separator)?;
        i = i + 1;
    }
    lines_written += i - 1;
    for x in trailer_content {
        out.write(x.as_bytes())?;
        out.write(line_separator)?;
        lines_written += 1;
    }
    out.flush()?;
    Ok(lines_written)
}

fn get_fmtd_line_number(width: &usize, line_number: usize, separator: &String) -> String {
    let line_str = line_number.to_string();
    if line_str.len() >= *width {
        format!("{:>width$}{}", &line_str[line_str.len() - *width..], separator, width = width)
    } else {
        format!("{:>width$}{}", line_str, separator, width = width)
    }
}

fn header_content(options: &OutputOptions, page: &usize) -> Vec<String> {
    let first_line: String = format!("{} {} Page {}", options.last_modified_time, options.header, page);
    vec!["".to_string(), "".to_string(), first_line, "".to_string(), "".to_string()]
}

fn file_last_modified_time(path: &str) -> String {
    let file_metadata = metadata(path);
    return file_metadata.map(|i| {
        return i.modified().map(|x| {
            let datetime: DateTime<Local> = x.into();
            datetime.format("%b %d %H:%M %Y").to_string()
        }).unwrap_or(String::new());
    }).unwrap_or(String::new());
}

fn current_time() -> String {
    let datetime: DateTime<Local> = Local::now();
    datetime.format("%b %d %H:%M %Y").to_string()
}

fn trailer_content() -> Vec<String> {
    vec!["".to_string(), "".to_string(), "".to_string(), "".to_string(), "".to_string()]
}

fn get_input_type(path: &str) -> Option<InputType> {
    if path == FILE_STDIN {
        return Some(InputType::StdIn);
    }

    metadata(path).map(|i| {
        match i.file_type() {
            #[cfg(unix)]
            ft if ft.is_block_device() =>
                {
                    Some(InputType::BlockDevice)
                }
            #[cfg(unix)]
            ft if ft.is_char_device() =>
                {
                    Some(InputType::CharacterDevice)
                }
            #[cfg(unix)]
            ft if ft.is_fifo() =>
                {
                    Some(InputType::Fifo)
                }
            #[cfg(unix)]
            ft if ft.is_socket() =>
                {
                    Some(InputType::Socket)
                }
            ft if ft.is_dir() => Some(InputType::Directory),
            ft if ft.is_file() => Some(InputType::File),
            ft if ft.is_symlink() => Some(InputType::SymLink),
            _ => None
        }
    }).unwrap_or(None)
}
<|endoftext|>


<commit_before>use queryst::parse;
use std::collections::HashMap;

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct PageParams {
    pub size: i64,
    pub number: i64,
}

/// JSON-API Query parameters
#[derive(Debug, PartialEq)]
pub struct Query {
    pub _type: String,
    pub include: Option<Vec<String>>,
    pub fields: Option<HashMap<String, Vec<String>>>,
    pub page: Option<PageParams>,
}

/// JSON-API Query parameters
impl Query {
    ///
    /// Takes a query parameter string and returns a Query
    ///
    /// ```
    /// use jsonapi::query::Query;
    /// let query = Query::from_params("include=author&fields[articles]=title,\
    ///                                 body&fields[people]=name&page[number]=3&page[size]=1");
    /// match query.include {
    ///     None => assert!(false),
    ///     Some(include) => {
    ///         assert_eq!(include.len(), 1);
    ///         assert_eq!(include[0], "author");
    ///     }
    /// }
    ///
    /// ```
    pub fn from_params(params: &str) -> Self {

        match parse(params) {
            Ok(o) => {
                let include = match o.find("include") {
                    None => None,
                    Some(inc) => {
                        match inc.as_str() {
                            None => None,
                            Some(include_str) => {
                                let arr: Vec<String> =
                                    include_str.split(',').map(|s| s.to_string()).collect();
                                Some(arr)
                            }
                        }
                    }
                };

                let mut fields = HashMap::<String, Vec<String>>::new();

                o.find("fields").map(|x| if x.is_object() {
                    x.as_object().map(|obj| for (key, value) in obj.iter() {
                        let arr: Vec<String> = match value.as_str() {
                            Some(string) => string.split(',').map(|s| s.to_string()).collect(),
                            None => Vec::<String>::new(),
                        };
                        fields.insert(key.to_string(), arr);

                    });
                } else {
                    error!("Query::from_params : No fields found in {:?}", x);
                });

                let page = PageParams {
                    number: match o.find_path(&["page", "number"]) {
                        None => 0,
                        Some(num) => {
                            if num.is_string() {
                                match num.as_str().map(str::parse::<i64>) {
                                    Some(y) => y.unwrap_or(0),
                                    None => 0,
                                }
                            } else {
                                0
                            }
                        }
                    },
                    size: match o.find_path(&["page", "size"]) {
                        None => 0,
                        Some(num) => {
                            if num.is_string() {
                                match num.as_str().map(str::parse::<i64>) {
                                    Some(y) => y.unwrap_or(0),
                                    None => 0,
                                }
                            } else {
                                0
                            }
                        }
                    },
                };

                Query {
                    _type: "none".into(),
                    include: include,
                    fields: Some(fields),
                    page: Some(page),
                }
            }
            Err(err) => {
                error!("Query::from_params : Can't parse : {:?}", err);
                Query {
                    _type: "none".into(),
                    include: None,
                    fields: None,
                    page: None,
                }
            }
        }
    }

    ///
    /// Builds a query parameter string from a Query
    ///
    /// ```
    /// use jsonapi::query::{Query, PageParams};
    /// let query = Query {
    ///   _type: "post".into(),
    ///   include: Some(vec!["author".into()]),
    ///   fields: None,
    ///   page: Some(PageParams {
    ///     size: 5,
    ///     number: 10,
    ///   }),
    /// };
    ///
    /// let query_string = query.to_params();
    /// assert_eq!(query_string, "include=author&page[size]=5&page[number]=10");
    ///
    /// ```
    pub fn to_params(&self) -> String {
        let mut params = Vec::<String>::new();

        if let Some(ref include) = self.include {
            params.push(format!("include={}", include.join(",")));
        }

        // Examples from json-api.org,
        // fields[articles]=title,body,author&fields[people]=name
        // fields[articles]=title,body&fields[people]=name

        if let Some(ref fields) = self.fields {
            for (name, val) in fields.iter() {
                params.push(format!("fields[{}]={}", name, val.join(",")));
            }
        }

        if let Some(ref page) = self.page {
            params.push(page.to_params());
        }

        params.join("&")
    }
}

impl PageParams {
    pub fn to_params(&self) -> String {
        format!("page[size]={}&page[number]={}", self.size, self.number)
    }
}
<commit_msg>feature: Add warn logs for setting query defaults<commit_after>use queryst::parse;
use std::collections::HashMap;

#[derive(Debug, PartialEq, Clone, Copy)]
pub struct PageParams {
    pub size: i64,
    pub number: i64,
}

/// JSON-API Query parameters
#[derive(Debug, PartialEq)]
pub struct Query {
    pub _type: String,
    pub include: Option<Vec<String>>,
    pub fields: Option<HashMap<String, Vec<String>>>,
    pub page: Option<PageParams>,
}

/// JSON-API Query parameters
impl Query {
    ///
    /// Takes a query parameter string and returns a Query
    ///
    /// ```
    /// use jsonapi::query::Query;
    /// let query = Query::from_params("include=author&fields[articles]=title,\
    ///                                 body&fields[people]=name&page[number]=3&page[size]=1");
    /// match query.include {
    ///     None => assert!(false),
    ///     Some(include) => {
    ///         assert_eq!(include.len(), 1);
    ///         assert_eq!(include[0], "author");
    ///     }
    /// }
    ///
    /// ```
    pub fn from_params(params: &str) -> Self {

        match parse(params) {
            Ok(o) => {
                let include = match o.find("include") {
                    None => None,
                    Some(inc) => {
                        match inc.as_str() {
                            None => None,
                            Some(include_str) => {
                                let arr: Vec<String> =
                                    include_str.split(',').map(|s| s.to_string()).collect();
                                Some(arr)
                            }
                        }
                    }
                };

                let mut fields = HashMap::<String, Vec<String>>::new();

                o.find("fields").map(|x| if x.is_object() {
                    x.as_object().map(|obj| for (key, value) in obj.iter() {
                        let arr: Vec<String> = match value.as_str() {
                            Some(string) => string.split(',').map(|s| s.to_string()).collect(),
                            None => Vec::<String>::new(),
                        };
                        fields.insert(key.to_string(), arr);

                    });
                } else {
                    error!("Query::from_params : No fields found in {:?}", x);
                });

                let page = PageParams {
                    number: match o.find_path(&["page", "number"]) {
                        None => {
                            warn!("Query::from_params : No page/number found in {:?}, setting \
                                   default 0",
                                  o);
                            0
                        }
                        Some(num) => {
                            if num.is_string() {
                                match num.as_str().map(str::parse::<i64>) {
                                    Some(y) => y.unwrap_or(0),
                                    None => {
                                        warn!("Query::from_params : page/number found in {:?}, \
                                               not able not able to parse it - setting default 0",
                                              o);
                                        0
                                    }
                                }
                            } else {
                                warn!("Query::from_params : page/number found in {:?}, but it is \
                                       not an expected type - setting default 0",
                                      o);
                                0
                            }
                        }
                    },
                    size: match o.find_path(&["page", "size"]) {
                        None => {
                            warn!("Query::from_params : No page/size found in {:?}, setting \
                                   default 0",
                                  o);
                            0
                        }
                        Some(num) => {
                            if num.is_string() {
                                match num.as_str().map(str::parse::<i64>) {
                                    Some(y) => y.unwrap_or(0),
                                    None => {
                                        warn!("Query::from_params : page/size found in {:?}, \
                                               not able not able to parse it - setting default 0",
                                              o);
                                        0
                                    }
                                }
                            } else {
                                warn!("Query::from_params : page/size found in {:?}, but it is \
                                       not an expected type - setting default 0",
                                      o);
                                0
                            }
                        }
                    },
                };

                Query {
                    _type: "none".into(),
                    include: include,
                    fields: Some(fields),
                    page: Some(page),
                }
            }
            Err(err) => {
                error!("Query::from_params : Can't parse : {:?}", err);
                Query {
                    _type: "none".into(),
                    include: None,
                    fields: None,
                    page: None,
                }
            }
        }
    }

    ///
    /// Builds a query parameter string from a Query
    ///
    /// ```
    /// use jsonapi::query::{Query, PageParams};
    /// let query = Query {
    ///   _type: "post".into(),
    ///   include: Some(vec!["author".into()]),
    ///   fields: None,
    ///   page: Some(PageParams {
    ///     size: 5,
    ///     number: 10,
    ///   }),
    /// };
    ///
    /// let query_string = query.to_params();
    /// assert_eq!(query_string, "include=author&page[size]=5&page[number]=10");
    ///
    /// ```
    pub fn to_params(&self) -> String {
        let mut params = Vec::<String>::new();

        if let Some(ref include) = self.include {
            params.push(format!("include={}", include.join(",")));
        }

        // Examples from json-api.org,
        // fields[articles]=title,body,author&fields[people]=name
        // fields[articles]=title,body&fields[people]=name

        if let Some(ref fields) = self.fields {
            for (name, val) in fields.iter() {
                params.push(format!("fields[{}]={}", name, val.join(",")));
            }
        }

        if let Some(ref page) = self.page {
            params.push(page.to_params());
        }

        params.join("&")
    }
}

impl PageParams {
    pub fn to_params(&self) -> String {
        format!("page[size]={}&page[number]={}", self.size, self.number)
    }
}
<|endoftext|>


<commit_before>//! This module provides a token source (`GetToken`) that obtains tokens for service accounts.
//! Service accounts are usually used by software (i.e., non-human actors) to get access to
//! resources. Currently, this module only works with RS256 JWTs, which makes it at least suitable for
//! authentication with Google services.
//!
//! Resources:
//! - [Using OAuth 2.0 for Server to Server
//! Applications](https://developers.google.com/identity/protocols/OAuth2ServiceAccount)
//! - [JSON Web Tokens](https://jwt.io/)
//!
//! Copyright (c) 2016 Google Inc (lewinb@google.com).
//!

use std::borrow::BorrowMut;
use std::default::Default;
use std::error;
use std::io::Read;
use std::result;
use std::str;

use authenticator::GetToken;
use storage::{hash_scopes, MemoryStorage, TokenStorage};
use types::{StringError, Token};

use hyper::header;
use url::form_urlencoded;

use base64;
use chrono;
use hyper;
use openssl;
use serde_json;

const GRANT_TYPE: &'static str = "urn:ietf:params:oauth:grant-type:jwt-bearer";
const GOOGLE_RS256_HEAD: &'static str = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";

// Encodes s as Base64
fn encode_base64<T: AsRef<[u8]>>(s: T) -> String {
    base64::encode_mode(s.as_ref(), base64::Base64Mode::UrlSafe)
}

fn decode_rsa_key(pem_pkcs8: &str) -> Result<openssl::rsa::Rsa, Box<error::Error>> {
    let private = pem_pkcs8.to_string().replace("\\n", "\n");
    let private_key = openssl::rsa::Rsa::private_key_from_pem(&mut private.as_bytes());

    match private_key {
        Err(e) => Err(Box::new(e)),
        Ok(key) => Ok(key),
    }
}

/// JSON schema of secret service account key. You can obtain the key from
/// the Cloud Console at https://console.cloud.google.com/.
///
/// You can use `helpers::service_account_key_from_file()` as a quick way to read a JSON client
/// secret into a ServiceAccountKey.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ServiceAccountKey {
    #[serde(rename="type")]
    pub key_type: Option<String>,
    pub project_id: Option<String>,
    pub private_key_id: Option<String>,
    pub private_key: Option<String>,
    pub client_email: Option<String>,
    pub client_id: Option<String>,
    pub auth_uri: Option<String>,
    pub token_uri: Option<String>,
    pub auth_provier_x509_cert_url: Option<String>,
    pub client_x509_cert_url: Option<String>,
}

#[derive(Serialize, Debug)]
struct Claims {
    iss: String,
    aud: String,
    exp: i64,
    iat: i64,
    sub: Option<String>,
    scope: String,
}

struct JWT {
    header: String,
    claims: Claims,
}

impl JWT {
    fn new(claims: Claims) -> JWT {
        JWT {
            header: GOOGLE_RS256_HEAD.to_string(),
            claims: claims,
        }
    }
    // Encodes the first two parts (header and claims) to base64 and assembles them into a form
    // ready to be signed.
    fn encode_claims(&self) -> String {
        let mut head = encode_base64(&self.header);
        let claims = encode_base64(serde_json::to_string(&self.claims).unwrap());

        head.push_str(".");
        head.push_str(&claims);
        head
    }

    fn sign(&self, private_key: &str) -> Result<String, Box<error::Error>> {
        let mut jwt_head = self.encode_claims();
        let key = try!(decode_rsa_key(private_key));
        let pkey = try!(openssl::pkey::PKey::from_rsa(key));

        let mut signer =
            try!(openssl::sign::Signer::new(
                 openssl::hash::MessageDigest::sha256(), &pkey));
        signer.update(&jwt_head.as_bytes()).unwrap();
        let signature = signer.finish().unwrap();
        let signature_b64 = encode_base64(signature);

        jwt_head.push_str(".");
        jwt_head.push_str(&signature_b64);

        Ok(jwt_head)
    }
}

fn init_claims_from_key<'a, I, T>(key: &ServiceAccountKey, scopes: I) -> Claims
    where T: AsRef<str> + 'a,
          I: IntoIterator<Item = &'a T>
{
    let iat = chrono::UTC::now().timestamp();
    let expiry = iat + 3600 - 5; // Max validity is 1h.

    let mut scopes_string = scopes.into_iter().fold(String::new(), |mut acc, sc| {
        acc.push_str(sc.as_ref());
        acc.push_str(" ");
        acc
    });
    scopes_string.pop();

    Claims {
        iss: key.client_email.clone().unwrap(),
        aud: key.token_uri.clone().unwrap(),
        exp: expiry,
        iat: iat,
        sub: None,
        scope: scopes_string,
    }
}

/// See "Additional claims" at https://developers.google.com/identity/protocols/OAuth2ServiceAccount
#[allow(dead_code)]
fn set_sub_claim(mut claims: Claims, sub: String) -> Claims {
    claims.sub = Some(sub);
    claims
}

/// A token source (`GetToken`) yielding OAuth tokens for services that use ServiceAccount authorization.
/// This token source caches token and automatically renews expired ones.
pub struct ServiceAccountAccess<C> {
    client: C,
    key: ServiceAccountKey,
    cache: MemoryStorage,
    sub: Option<String>,
}

/// This is the schema of the server's response.
#[derive(Deserialize, Debug)]
struct TokenResponse {
    access_token: Option<String>,
    token_type: Option<String>,
    expires_in: Option<i64>,
}

impl TokenResponse {
    fn to_oauth_token(self) -> Token {
        let expires_ts = chrono::UTC::now().timestamp() + self.expires_in.unwrap_or(0);

        Token {
            access_token: self.access_token.unwrap(),
            token_type: self.token_type.unwrap(),
            refresh_token: String::new(),
            expires_in: self.expires_in,
            expires_in_timestamp: Some(expires_ts),
        }
    }
}

impl<'a, C> ServiceAccountAccess<C>
    where C: BorrowMut<hyper::Client>
{
    /// Returns a new `ServiceAccountAccess` token source.
    #[allow(dead_code)]
    pub fn new(key: ServiceAccountKey, client: C) -> ServiceAccountAccess<C> {
        ServiceAccountAccess {
            client: client,
            key: key,
            cache: MemoryStorage::default(),
            sub: None,
        }
    }

    pub fn with_sub(key: ServiceAccountKey, client: C, sub: Option<String>) -> ServiceAccountAccess<C> {
        ServiceAccountAccess {
            client: client,
            key: key,
            cache: MemoryStorage::default(),
            sub: sub,
        }
    }

    fn request_token(&mut self, scopes: &Vec<&str>) -> result::Result<Token, Box<error::Error>> {
        let mut claims = init_claims_from_key(&self.key, scopes);
        claims.sub = self.sub.clone();
        let signed = try!(JWT::new(claims)
            .sign(self.key.private_key.as_ref().unwrap()));

        let body = form_urlencoded::serialize(vec![("grant_type".to_string(),
                                                    GRANT_TYPE.to_string()),
                                                   ("assertion".to_string(), signed)]);

        let mut response = String::new();
        let mut result = try!(self.client
            .borrow_mut()
            .post(self.key.token_uri.as_ref().unwrap())
            .body(&body)
            .header(header::ContentType("application/x-www-form-urlencoded".parse().unwrap()))
            .send());

        try!(result.read_to_string(&mut response));

        let token: Result<TokenResponse, serde_json::error::Error> =
            serde_json::from_str(&response);

        match token {
            Err(e) => return Err(Box::new(e)),
            Ok(token) => {
                if token.access_token.is_none() || token.token_type.is_none() ||
                   token.expires_in.is_none() {
                    Err(Box::new(StringError::new("Token response lacks fields".to_string(),
                                                  Some(&format!("{:?}", token)))))
                } else {
                    Ok(token.to_oauth_token())
                }
            }
        }
    }
}

impl<C: BorrowMut<hyper::Client>> GetToken for ServiceAccountAccess<C> {
    fn token<'b, I, T>(&mut self, scopes: I) -> result::Result<Token, Box<error::Error>>
        where T: AsRef<str> + Ord + 'b,
              I: IntoIterator<Item = &'b T>
    {
        let (hash, scps) = hash_scopes(scopes);

        if let Some(token) = try!(self.cache.get(hash, &scps)) {
            if !token.expired() {
                return Ok(token);
            }
        }

        let token = try!(self.request_token(&scps));
        let _ = self.cache.set(hash, &scps, Some(token.clone()));

        Ok(token)
    }

    fn api_key(&mut self) -> Option<String> {
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use helper::service_account_key_from_file;
    use hyper;
    use hyper::net::HttpsConnector;
    use hyper_rustls;
    use authenticator::GetToken;

    // This is a valid but deactivated key.
    const TEST_PRIVATE_KEY_PATH: &'static str = "examples/Sanguine-69411a0c0eea.json";

    // Uncomment this test to verify that we can successfully obtain tokens.
    //#[test]
    #[allow(dead_code)]
    fn test_service_account_e2e() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let client = hyper::Client::with_connector(HttpsConnector::new(hyper_rustls::TlsClient::new()));
        let mut acc = ServiceAccountAccess::new(key, client);
        println!("{:?}",
                 acc.token(vec![&"https://www.googleapis.com/auth/pubsub"]).unwrap());
    }

    #[test]
    fn test_jwt_initialize_claims() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let scopes = vec!["scope1", "scope2", "scope3"];
        let claims = super::init_claims_from_key(&key, &scopes);

        assert_eq!(claims.iss,
                   "oauth2-public-test@sanguine-rhythm-105020.iam.gserviceaccount.com".to_string());
        assert_eq!(claims.scope, "scope1 scope2 scope3".to_string());
        assert_eq!(claims.aud,
                   "https://accounts.google.com/o/oauth2/token".to_string());
        assert!(claims.exp > 1000000000);
        assert!(claims.iat < claims.exp);
        assert_eq!(claims.exp - claims.iat, 3595);
    }

    #[test]
    fn test_jwt_sign() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let scopes = vec!["scope1", "scope2", "scope3"];
        let claims = super::init_claims_from_key(&key, &scopes);
        let jwt = super::JWT::new(claims);
        let signature = jwt.sign(key.private_key.as_ref().unwrap());

        assert!(signature.is_ok());

        let signature = signature.unwrap();
        assert_eq!(signature.split(".").nth(0).unwrap(),
                   "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9");
    }
}
<commit_msg>Make "sub" argument simple String in with_sub constructor<commit_after>//! This module provides a token source (`GetToken`) that obtains tokens for service accounts.
//! Service accounts are usually used by software (i.e., non-human actors) to get access to
//! resources. Currently, this module only works with RS256 JWTs, which makes it at least suitable for
//! authentication with Google services.
//!
//! Resources:
//! - [Using OAuth 2.0 for Server to Server
//! Applications](https://developers.google.com/identity/protocols/OAuth2ServiceAccount)
//! - [JSON Web Tokens](https://jwt.io/)
//!
//! Copyright (c) 2016 Google Inc (lewinb@google.com).
//!

use std::borrow::BorrowMut;
use std::default::Default;
use std::error;
use std::io::Read;
use std::result;
use std::str;

use authenticator::GetToken;
use storage::{hash_scopes, MemoryStorage, TokenStorage};
use types::{StringError, Token};

use hyper::header;
use url::form_urlencoded;

use base64;
use chrono;
use hyper;
use openssl;
use serde_json;

const GRANT_TYPE: &'static str = "urn:ietf:params:oauth:grant-type:jwt-bearer";
const GOOGLE_RS256_HEAD: &'static str = "{\"alg\":\"RS256\",\"typ\":\"JWT\"}";

// Encodes s as Base64
fn encode_base64<T: AsRef<[u8]>>(s: T) -> String {
    base64::encode_mode(s.as_ref(), base64::Base64Mode::UrlSafe)
}

fn decode_rsa_key(pem_pkcs8: &str) -> Result<openssl::rsa::Rsa, Box<error::Error>> {
    let private = pem_pkcs8.to_string().replace("\\n", "\n");
    let private_key = openssl::rsa::Rsa::private_key_from_pem(&mut private.as_bytes());

    match private_key {
        Err(e) => Err(Box::new(e)),
        Ok(key) => Ok(key),
    }
}

/// JSON schema of secret service account key. You can obtain the key from
/// the Cloud Console at https://console.cloud.google.com/.
///
/// You can use `helpers::service_account_key_from_file()` as a quick way to read a JSON client
/// secret into a ServiceAccountKey.
#[derive(Serialize, Deserialize, Debug, Clone)]
pub struct ServiceAccountKey {
    #[serde(rename="type")]
    pub key_type: Option<String>,
    pub project_id: Option<String>,
    pub private_key_id: Option<String>,
    pub private_key: Option<String>,
    pub client_email: Option<String>,
    pub client_id: Option<String>,
    pub auth_uri: Option<String>,
    pub token_uri: Option<String>,
    pub auth_provier_x509_cert_url: Option<String>,
    pub client_x509_cert_url: Option<String>,
}

#[derive(Serialize, Debug)]
struct Claims {
    iss: String,
    aud: String,
    exp: i64,
    iat: i64,
    sub: Option<String>,
    scope: String,
}

struct JWT {
    header: String,
    claims: Claims,
}

impl JWT {
    fn new(claims: Claims) -> JWT {
        JWT {
            header: GOOGLE_RS256_HEAD.to_string(),
            claims: claims,
        }
    }
    // Encodes the first two parts (header and claims) to base64 and assembles them into a form
    // ready to be signed.
    fn encode_claims(&self) -> String {
        let mut head = encode_base64(&self.header);
        let claims = encode_base64(serde_json::to_string(&self.claims).unwrap());

        head.push_str(".");
        head.push_str(&claims);
        head
    }

    fn sign(&self, private_key: &str) -> Result<String, Box<error::Error>> {
        let mut jwt_head = self.encode_claims();
        let key = try!(decode_rsa_key(private_key));
        let pkey = try!(openssl::pkey::PKey::from_rsa(key));

        let mut signer =
            try!(openssl::sign::Signer::new(
                 openssl::hash::MessageDigest::sha256(), &pkey));
        signer.update(&jwt_head.as_bytes()).unwrap();
        let signature = signer.finish().unwrap();
        let signature_b64 = encode_base64(signature);

        jwt_head.push_str(".");
        jwt_head.push_str(&signature_b64);

        Ok(jwt_head)
    }
}

fn init_claims_from_key<'a, I, T>(key: &ServiceAccountKey, scopes: I) -> Claims
    where T: AsRef<str> + 'a,
          I: IntoIterator<Item = &'a T>
{
    let iat = chrono::UTC::now().timestamp();
    let expiry = iat + 3600 - 5; // Max validity is 1h.

    let mut scopes_string = scopes.into_iter().fold(String::new(), |mut acc, sc| {
        acc.push_str(sc.as_ref());
        acc.push_str(" ");
        acc
    });
    scopes_string.pop();

    Claims {
        iss: key.client_email.clone().unwrap(),
        aud: key.token_uri.clone().unwrap(),
        exp: expiry,
        iat: iat,
        sub: None,
        scope: scopes_string,
    }
}

/// See "Additional claims" at https://developers.google.com/identity/protocols/OAuth2ServiceAccount
#[allow(dead_code)]
fn set_sub_claim(mut claims: Claims, sub: String) -> Claims {
    claims.sub = Some(sub);
    claims
}

/// A token source (`GetToken`) yielding OAuth tokens for services that use ServiceAccount authorization.
/// This token source caches token and automatically renews expired ones.
pub struct ServiceAccountAccess<C> {
    client: C,
    key: ServiceAccountKey,
    cache: MemoryStorage,
    sub: Option<String>,
}

/// This is the schema of the server's response.
#[derive(Deserialize, Debug)]
struct TokenResponse {
    access_token: Option<String>,
    token_type: Option<String>,
    expires_in: Option<i64>,
}

impl TokenResponse {
    fn to_oauth_token(self) -> Token {
        let expires_ts = chrono::UTC::now().timestamp() + self.expires_in.unwrap_or(0);

        Token {
            access_token: self.access_token.unwrap(),
            token_type: self.token_type.unwrap(),
            refresh_token: String::new(),
            expires_in: self.expires_in,
            expires_in_timestamp: Some(expires_ts),
        }
    }
}

impl<'a, C> ServiceAccountAccess<C>
    where C: BorrowMut<hyper::Client>
{
    /// Returns a new `ServiceAccountAccess` token source.
    #[allow(dead_code)]
    pub fn new(key: ServiceAccountKey, client: C) -> ServiceAccountAccess<C> {
        ServiceAccountAccess {
            client: client,
            key: key,
            cache: MemoryStorage::default(),
            sub: None,
        }
    }

    pub fn with_sub(key: ServiceAccountKey, client: C, sub: String) -> ServiceAccountAccess<C> {
        ServiceAccountAccess {
            client: client,
            key: key,
            cache: MemoryStorage::default(),
            sub: Some(sub),
        }
    }

    fn request_token(&mut self, scopes: &Vec<&str>) -> result::Result<Token, Box<error::Error>> {
        let mut claims = init_claims_from_key(&self.key, scopes);
        claims.sub = self.sub.clone();
        let signed = try!(JWT::new(claims)
            .sign(self.key.private_key.as_ref().unwrap()));

        let body = form_urlencoded::serialize(vec![("grant_type".to_string(),
                                                    GRANT_TYPE.to_string()),
                                                   ("assertion".to_string(), signed)]);

        let mut response = String::new();
        let mut result = try!(self.client
            .borrow_mut()
            .post(self.key.token_uri.as_ref().unwrap())
            .body(&body)
            .header(header::ContentType("application/x-www-form-urlencoded".parse().unwrap()))
            .send());

        try!(result.read_to_string(&mut response));

        let token: Result<TokenResponse, serde_json::error::Error> =
            serde_json::from_str(&response);

        match token {
            Err(e) => return Err(Box::new(e)),
            Ok(token) => {
                if token.access_token.is_none() || token.token_type.is_none() ||
                   token.expires_in.is_none() {
                    Err(Box::new(StringError::new("Token response lacks fields".to_string(),
                                                  Some(&format!("{:?}", token)))))
                } else {
                    Ok(token.to_oauth_token())
                }
            }
        }
    }
}

impl<C: BorrowMut<hyper::Client>> GetToken for ServiceAccountAccess<C> {
    fn token<'b, I, T>(&mut self, scopes: I) -> result::Result<Token, Box<error::Error>>
        where T: AsRef<str> + Ord + 'b,
              I: IntoIterator<Item = &'b T>
    {
        let (hash, scps) = hash_scopes(scopes);

        if let Some(token) = try!(self.cache.get(hash, &scps)) {
            if !token.expired() {
                return Ok(token);
            }
        }

        let token = try!(self.request_token(&scps));
        let _ = self.cache.set(hash, &scps, Some(token.clone()));

        Ok(token)
    }

    fn api_key(&mut self) -> Option<String> {
        None
    }
}

#[cfg(test)]
mod tests {
    use super::*;
    use helper::service_account_key_from_file;
    use hyper;
    use hyper::net::HttpsConnector;
    use hyper_rustls;
    use authenticator::GetToken;

    // This is a valid but deactivated key.
    const TEST_PRIVATE_KEY_PATH: &'static str = "examples/Sanguine-69411a0c0eea.json";

    // Uncomment this test to verify that we can successfully obtain tokens.
    //#[test]
    #[allow(dead_code)]
    fn test_service_account_e2e() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let client = hyper::Client::with_connector(HttpsConnector::new(hyper_rustls::TlsClient::new()));
        let mut acc = ServiceAccountAccess::new(key, client);
        println!("{:?}",
                 acc.token(vec![&"https://www.googleapis.com/auth/pubsub"]).unwrap());
    }

    #[test]
    fn test_jwt_initialize_claims() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let scopes = vec!["scope1", "scope2", "scope3"];
        let claims = super::init_claims_from_key(&key, &scopes);

        assert_eq!(claims.iss,
                   "oauth2-public-test@sanguine-rhythm-105020.iam.gserviceaccount.com".to_string());
        assert_eq!(claims.scope, "scope1 scope2 scope3".to_string());
        assert_eq!(claims.aud,
                   "https://accounts.google.com/o/oauth2/token".to_string());
        assert!(claims.exp > 1000000000);
        assert!(claims.iat < claims.exp);
        assert_eq!(claims.exp - claims.iat, 3595);
    }

    #[test]
    fn test_jwt_sign() {
        let key = service_account_key_from_file(&TEST_PRIVATE_KEY_PATH.to_string()).unwrap();
        let scopes = vec!["scope1", "scope2", "scope3"];
        let claims = super::init_claims_from_key(&key, &scopes);
        let jwt = super::JWT::new(claims);
        let signature = jwt.sign(key.private_key.as_ref().unwrap());

        assert!(signature.is_ok());

        let signature = signature.unwrap();
        assert_eq!(signature.split(".").nth(0).unwrap(),
                   "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9");
    }
}
<|endoftext|>


<commit_before>pub type QueryPair = (String, String);

pub trait QueryString {
    fn as_query_kv(&self) -> Vec<QueryPair>;

    fn as_query(&self) -> String {
        let params: Vec<String> = self.as_query_kv().iter()
            .map(|&(ref k, ref v)| format!("{}={}", k, v))
            .collect();

        format!("?{}", params.join("&"))
    }
}

pub trait QueryParameter {
    fn as_query_kv(&self, &str) -> Vec<QueryPair>;
}

impl<T: QueryString> QueryString for Option<T> {
    fn as_query_kv(&self) -> Vec<QueryPair> {
        match *self {
            Some(ref thing) => thing.as_query_kv(),
            None => Vec::new(),
        }
    }
}

impl<T: QueryParameter> QueryParameter for Option<T> {
    fn as_query_kv(&self, key: &str) -> Vec<QueryPair> {
        match *self {
            Some(ref thing) => thing.as_query_kv(key),
            None => vec![(key.into(), "".into())],
        }
    }
}

impl<T: QueryParameter> QueryParameter for Vec<T> {
    fn as_query_kv(&self, key: &str) -> Vec<QueryPair> {
        let mut query_pairs: Vec<QueryPair> = Vec::new();
        for item in self.iter() {
            query_pairs.append(&mut item.as_query_kv(key));
        }
        query_pairs
    }
}
<commit_msg>Fix double `?` in query string<commit_after>pub type QueryPair = (String, String);

pub trait QueryString {
    fn as_query_kv(&self) -> Vec<QueryPair>;

    fn as_query(&self) -> String {
        self.as_query_kv().iter()
            .map(|&(ref k, ref v)| format!("{}={}", k, v))
            .collect::<Vec<String>>()
            .join("&")
    }
}

pub trait QueryParameter {
    fn as_query_kv(&self, &str) -> Vec<QueryPair>;
}

impl<T: QueryString> QueryString for Option<T> {
    fn as_query_kv(&self) -> Vec<QueryPair> {
        match *self {
            Some(ref thing) => thing.as_query_kv(),
            None => Vec::new(),
        }
    }
}

impl<T: QueryParameter> QueryParameter for Option<T> {
    fn as_query_kv(&self, key: &str) -> Vec<QueryPair> {
        match *self {
            Some(ref thing) => thing.as_query_kv(key),
            None => vec![(key.into(), "".into())],
        }
    }
}

impl<T: QueryParameter> QueryParameter for Vec<T> {
    fn as_query_kv(&self, key: &str) -> Vec<QueryPair> {
        let mut query_pairs: Vec<QueryPair> = Vec::new();
        for item in self.iter() {
            query_pairs.append(&mut item.as_query_kv(key));
        }
        query_pairs
    }
}
<|endoftext|>


<commit_before>pub type BoxedScene<EventT, RendererT, EngineDataT, SceneChangeDataT> = Box<Scene<EventT, RendererT, EngineDataT, SceneChangeDataT> + 'static>;
pub type SceneChangeCallback<EventT, RendererT, EngineDataT, SceneChangeDataT> = Fn(&SceneChangeDataT, &mut RendererT, &mut EngineDataT) -> BoxedScene<EventT, RendererT, EngineDataT, SceneChangeDataT>;

pub trait Scene<EventT, RendererT, EngineDataT, SceneChangeDataT> {
    fn render(&self, renderer: &mut RendererT, engine_data: &EngineDataT, tick: u64);
    fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64);
    fn think(&mut self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) -> Option<SceneChangeEvent<SceneChangeDataT>>;
}

pub enum SceneChangeEvent<SceneChangeDataT> {
    PushScene(SceneChangeDataT),
    SwapScene(SceneChangeDataT),
    PopScene,
}

pub struct SceneStack<EventT, RendererT, EngineDataT, SceneChangeDataT> {
    scenes: Vec<BoxedScene<EventT, RendererT, EngineDataT, SceneChangeDataT>>
}

impl<EventT, RendererT, EngineDataT, SceneChangeDataT> SceneStack<EventT, RendererT, EngineDataT, SceneChangeDataT> {

    pub fn new() -> SceneStack<EngineDataT, EventT, RendererT, SceneChangeDataT> {
        SceneStack {
            scenes: vec![]
        }
    }

    pub fn is_empty(&self) -> bool {
        self.scenes.len() == 0
    }

    pub fn push(&mut self, scene: BoxedScene<EventT, RendererT, EngineDataT, SceneChangeDataT>) {
        self.scenes.push(scene)
    }

    pub fn swap(&mut self, scene: BoxedScene<EventT, RendererT, EngineDataT, SceneChangeDataT>) -> Option<BoxedScene<EventT, RendererT, EngineDataT, SceneChangeDataT>> {
        let old_scene = self.scenes.pop();
        self.scenes.push(scene);
        old_scene
    }

    pub fn pop(&mut self) -> Option<BoxedScene<EventT, RendererT, EngineDataT, SceneChangeDataT>> {
        self.scenes.pop()
    }

    pub fn render(&mut self, renderer: &mut RendererT, engine_data: &EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(scene) => {
                scene.render(renderer, engine_data, tick);
                self.scenes.push(scene);
            },
            None => ()
        }
    }

    pub fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(mut scene) => {
                scene.handle_event(event, renderer, engine_data, tick);
                self.scenes.push(scene);
            },
            None => ()
        };
    }

    pub fn think(&mut self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64, scene_change_handler: &SceneChangeCallback<EventT, RendererT, EngineDataT, SceneChangeDataT>) {
        let maybe_last_scene = self.scenes.pop();
        let event = match maybe_last_scene {
            Some(mut scene) => {
                let event = scene.think(renderer, engine_data, tick);
                self.scenes.push(scene);
                event
            },
            None => None
        };

        match event {
            Some(SceneChangeEvent::PushScene(ref data)) => {
                self.push(scene_change_handler(data, renderer, engine_data));
            },
            Some(SceneChangeEvent::SwapScene(ref data)) => {
                self.swap(scene_change_handler(data, renderer, engine_data));
            },
            Some(SceneChangeEvent::PopScene) => {
                self.pop();
            },
            None => ()
        };
    }
}
<commit_msg>Better lifetimes on Scene<commit_after>pub type BoxedScene<'a, EventT, RendererT, EngineDataT, SceneChangeDataT> = Box<Scene<EventT, RendererT, EngineDataT, SceneChangeDataT> + 'a>;
pub type SceneChangeCallback<'a, EventT, RendererT, EngineDataT, SceneChangeDataT> = Fn(&SceneChangeDataT, &mut RendererT, &mut EngineDataT) -> BoxedScene<'a, EventT, RendererT, EngineDataT, SceneChangeDataT>;

pub trait Scene<EventT, RendererT, EngineDataT, SceneChangeDataT> {
    fn render(&self, renderer: &mut RendererT, engine_data: &EngineDataT, tick: u64);
    fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64);
    fn think(&mut self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) -> Option<SceneChangeEvent<SceneChangeDataT>>;
}

pub enum SceneChangeEvent<SceneChangeDataT> {
    PushScene(SceneChangeDataT),
    SwapScene(SceneChangeDataT),
    PopScene,
}

pub struct SceneStack<'a, EventT, RendererT, EngineDataT, SceneChangeDataT> {
    scenes: Vec<BoxedScene<'a, EventT, RendererT, EngineDataT, SceneChangeDataT>>
}

impl<'a, EventT, RendererT, EngineDataT, SceneChangeDataT> SceneStack<'a, EventT, RendererT, EngineDataT, SceneChangeDataT> {

    pub fn new() -> SceneStack<'a, EngineDataT, EventT, RendererT, SceneChangeDataT> {
        SceneStack {
            scenes: vec![]
        }
    }

    pub fn is_empty(&self) -> bool {
        self.scenes.len() == 0
    }

    pub fn push(&mut self, scene: BoxedScene<'a, EventT, RendererT, EngineDataT, SceneChangeDataT>) {
        self.scenes.push(scene)
    }

    pub fn swap(&mut self, scene: BoxedScene<'a, EventT, RendererT, EngineDataT, SceneChangeDataT>) -> Option<BoxedScene<'a, EventT, RendererT, EngineDataT, SceneChangeDataT>> {
        let old_scene = self.scenes.pop();
        self.scenes.push(scene);
        old_scene
    }

    pub fn pop(&mut self) -> Option<BoxedScene<'a, EventT, RendererT, EngineDataT, SceneChangeDataT>> {
        self.scenes.pop()
    }

    pub fn render(&mut self, renderer: &mut RendererT, engine_data: &EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(scene) => {
                scene.render(renderer, engine_data, tick);
                self.scenes.push(scene);
            },
            None => ()
        }
    }

    pub fn handle_event(&mut self, event: &EventT, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64) {
        let maybe_last_scene = self.scenes.pop();
        match maybe_last_scene {
            Some(mut scene) => {
                scene.handle_event(event, renderer, engine_data, tick);
                self.scenes.push(scene);
            },
            None => ()
        };
    }

    pub fn think(&mut self, renderer: &mut RendererT, engine_data: &mut EngineDataT, tick: u64, scene_change_handler: &SceneChangeCallback<'a, EventT, RendererT, EngineDataT, SceneChangeDataT>) {
        let maybe_last_scene = self.scenes.pop();
        let event = match maybe_last_scene {
            Some(mut scene) => {
                let event = scene.think(renderer, engine_data, tick);
                self.scenes.push(scene);
                event
            },
            None => None
        };

        match event {
            Some(SceneChangeEvent::PushScene(ref data)) => {
                self.push(scene_change_handler(data, renderer, engine_data));
            },
            Some(SceneChangeEvent::SwapScene(ref data)) => {
                self.swap(scene_change_handler(data, renderer, engine_data));
            },
            Some(SceneChangeEvent::PopScene) => {
                self.pop();
            },
            None => ()
        };
    }
}
<|endoftext|>