<commit_before>//! Providing the features between "full" and "derive" of syn.
//!
//! This crate provides the following two unique data structures.
//!
//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed.
//!
//!   ```text
//!   fn process(n: usize) -> Result<()> { ... }
//!   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^     ^
//!   ```
//!
//! * [`syn_mid::Block`] -- A block whose body is not parsed.
//!
//!   ```text
//!   { ... }
//!   ^     ^
//!   ```
//!
//! Other data structures are the same as data structures of [syn]. These are defined in this crate
//! because they cannot be used in [syn] without "full" feature.
//!
//! ## Optional features
//!
//! syn-mid in the default features aims to provide the features between "full"
//! and "derive" of [syn].
//!
//! * **`clone-impls`** — Clone impls for all syntax tree types.
//!
//! [`syn_mid::ItemFn`]: struct.ItemFn.html
//! [`syn_mid::Block`]: struct.Block.html
//! [syn]: https://github.com/dtolnay/syn

#![doc(html_root_url = "https://docs.rs/syn-mid/0.5.0")]
#![doc(test(
    no_crate_inject,
    attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
))]
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]
// mem::take requires Rust 1.40
#![allow(clippy::mem_replace_with_default)]
#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)]

// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn.

#[macro_use]
mod macros;

mod arg;
mod pat;
mod path;

pub use self::{arg::*, pat::*};

use proc_macro2::TokenStream;
use syn::{
    punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, ReturnType, Token, Variadic,
    Visibility,
};

ast_struct! {
    /// A braced block containing Rust statements.
    pub struct Block {
        pub brace_token: token::Brace,
        /// Statements in a block
        pub stmts: TokenStream,
    }
}

ast_struct! {
    /// A free-standing function: `fn process(n: usize) -> Result<()> { ...
    /// }`.
    pub struct ItemFn {
        pub attrs: Vec<Attribute>,
        pub vis: Visibility,
        pub sig: Signature,
        pub block: Box<Block>,
    }
}

ast_struct! {
    /// A function signature in a trait or implementation: `unsafe fn
    /// initialize(&self)`.
    pub struct Signature {
        pub constness: Option<Token![const]>,
        pub asyncness: Option<Token![async]>,
        pub unsafety: Option<Token![unsafe]>,
        pub abi: Option<Abi>,
        pub fn_token: Token![fn],
        pub ident: Ident,
        pub generics: Generics,
        pub paren_token: token::Paren,
        pub inputs: Punctuated<FnArg, Token![,]>,
        pub variadic: Option<Variadic>,
        pub output: ReturnType,
    }
}

mod parsing {
    use syn::{
        braced, parenthesized,
        parse::{Parse, ParseStream, Result},
        parse2, Abi, Attribute, Generics, Ident, ReturnType, Token, Type, Variadic, Visibility,
        WhereClause,
    };

    use super::{Block, FnArg, ItemFn, PatType, Signature};

    impl Parse for Block {
        fn parse(input: ParseStream<'_>) -> Result<Self> {
            let content;
            Ok(Self { brace_token: braced!(content in input), stmts: content.parse()? })
        }
    }

    impl Parse for ItemFn {
        fn parse(input: ParseStream<'_>) -> Result<Self> {
            let attrs = input.call(Attribute::parse_outer)?;
            let vis: Visibility = input.parse()?;
            let constness: Option<Token![const]> = input.parse()?;
            let asyncness: Option<Token![async]> = input.parse()?;
            let unsafety: Option<Token![unsafe]> = input.parse()?;
            let abi: Option<Abi> = input.parse()?;
            let fn_token: Token![fn] = input.parse()?;
            let ident: Ident = input.parse()?;
            let generics: Generics = input.parse()?;

            let content;
            let paren_token = parenthesized!(content in input);
            let inputs = content.parse_terminated(FnArg::parse)?;
            let variadic = inputs.last().as_ref().and_then(get_variadic);

            #[allow(clippy::trivially_copy_pass_by_ref)]
            fn get_variadic(input: &&FnArg) -> Option<Variadic> {
                if let FnArg::Typed(PatType { ty, .. }) = input {
                    if let Type::Verbatim(tokens) = &**ty {
                        if let Ok(dots) = parse2(tokens.clone()) {
                            return Some(Variadic { attrs: Vec::new(), dots });
                        }
                    }
                }
                None
            }

            let output: ReturnType = input.parse()?;
            let where_clause: Option<WhereClause> = input.parse()?;

            let block = input.parse()?;

            Ok(Self {
                attrs,
                vis,
                sig: Signature {
                    constness,
                    asyncness,
                    unsafety,
                    abi,
                    fn_token,
                    ident,
                    paren_token,
                    inputs,
                    output,
                    variadic,
                    generics: Generics { where_clause, ..generics },
                },
                block: Box::new(block),
            })
        }
    }
}

mod printing {
    use proc_macro2::TokenStream;
    use quote::{ToTokens, TokenStreamExt};
    use syn::{punctuated::Punctuated, Token, Type};

    use super::{Block, FnArg, ItemFn, Signature};

    impl ToTokens for Block {
        fn to_tokens(&self, tokens: &mut TokenStream) {
            self.brace_token.surround(tokens, |tokens| {
                tokens.append_all(self.stmts.clone());
            });
        }
    }

    fn has_variadic(inputs: &Punctuated<FnArg, Token![,]>) -> bool {
        let last = match inputs.last() {
            Some(last) => last,
            None => return false,
        };

        let pat = match last {
            FnArg::Typed(pat) => pat,
            FnArg::Receiver(_) => return false,
        };

        let tokens = match pat.ty.as_ref() {
            Type::Verbatim(tokens) => tokens,
            _ => return false,
        };

        tokens.to_string() == "..."
    }

    impl ToTokens for Signature {
        fn to_tokens(&self, tokens: &mut TokenStream) {
            self.constness.to_tokens(tokens);
            self.asyncness.to_tokens(tokens);
            self.unsafety.to_tokens(tokens);
            self.abi.to_tokens(tokens);
            self.fn_token.to_tokens(tokens);
            self.ident.to_tokens(tokens);
            self.generics.to_tokens(tokens);
            self.paren_token.surround(tokens, |tokens| {
                self.inputs.to_tokens(tokens);
                if self.variadic.is_some() && !has_variadic(&self.inputs) {
                    if !self.inputs.empty_or_trailing() {
                        <Token![,]>::default().to_tokens(tokens);
                    }
                    self.variadic.to_tokens(tokens);
                }
            });
            self.output.to_tokens(tokens);
            self.generics.where_clause.to_tokens(tokens);
        }
    }

    impl ToTokens for ItemFn {
        fn to_tokens(&self, tokens: &mut TokenStream) {
            tokens.append_all(&self.attrs);
            self.vis.to_tokens(tokens);
            self.sig.to_tokens(tokens);
            self.block.brace_token.surround(tokens, |tokens| {
                tokens.append_all(self.block.stmts.clone());
            });
        }
    }
}
<commit_msg>Remove #[allow(clippy::trivially_copy_pass_by_ref)]<commit_after>//! Providing the features between "full" and "derive" of syn.
//!
//! This crate provides the following two unique data structures.
//!
//! * [`syn_mid::ItemFn`] -- A function whose body is not parsed.
//!
//!   ```text
//!   fn process(n: usize) -> Result<()> { ... }
//!   ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ ^     ^
//!   ```
//!
//! * [`syn_mid::Block`] -- A block whose body is not parsed.
//!
//!   ```text
//!   { ... }
//!   ^     ^
//!   ```
//!
//! Other data structures are the same as data structures of [syn]. These are defined in this crate
//! because they cannot be used in [syn] without "full" feature.
//!
//! ## Optional features
//!
//! syn-mid in the default features aims to provide the features between "full"
//! and "derive" of [syn].
//!
//! * **`clone-impls`** — Clone impls for all syntax tree types.
//!
//! [`syn_mid::ItemFn`]: struct.ItemFn.html
//! [`syn_mid::Block`]: struct.Block.html
//! [syn]: https://github.com/dtolnay/syn

#![doc(html_root_url = "https://docs.rs/syn-mid/0.5.0")]
#![doc(test(
    no_crate_inject,
    attr(deny(warnings, rust_2018_idioms, single_use_lifetimes), allow(dead_code))
))]
#![forbid(unsafe_code)]
#![warn(rust_2018_idioms, single_use_lifetimes, unreachable_pub)]
#![warn(clippy::all, clippy::default_trait_access)]
// mem::take requires Rust 1.40
#![allow(clippy::mem_replace_with_default)]
#![allow(clippy::eval_order_dependence, clippy::large_enum_variant)]

// Many of the code contained in this crate are copies from https://github.com/dtolnay/syn.

#[macro_use]
mod macros;

mod arg;
mod pat;
mod path;

pub use self::{arg::*, pat::*};

use proc_macro2::TokenStream;
use syn::{
    punctuated::Punctuated, token, Abi, Attribute, Generics, Ident, ReturnType, Token, Variadic,
    Visibility,
};

ast_struct! {
    /// A braced block containing Rust statements.
    pub struct Block {
        pub brace_token: token::Brace,
        /// Statements in a block
        pub stmts: TokenStream,
    }
}

ast_struct! {
    /// A free-standing function: `fn process(n: usize) -> Result<()> { ...
    /// }`.
    pub struct ItemFn {
        pub attrs: Vec<Attribute>,
        pub vis: Visibility,
        pub sig: Signature,
        pub block: Box<Block>,
    }
}

ast_struct! {
    /// A function signature in a trait or implementation: `unsafe fn
    /// initialize(&self)`.
    pub struct Signature {
        pub constness: Option<Token![const]>,
        pub asyncness: Option<Token![async]>,
        pub unsafety: Option<Token![unsafe]>,
        pub abi: Option<Abi>,
        pub fn_token: Token![fn],
        pub ident: Ident,
        pub generics: Generics,
        pub paren_token: token::Paren,
        pub inputs: Punctuated<FnArg, Token![,]>,
        pub variadic: Option<Variadic>,
        pub output: ReturnType,
    }
}

mod parsing {
    use syn::{
        braced, parenthesized,
        parse::{Parse, ParseStream, Result},
        parse2, Abi, Attribute, Generics, Ident, ReturnType, Token, Type, Variadic, Visibility,
        WhereClause,
    };

    use super::{Block, FnArg, ItemFn, PatType, Signature};

    impl Parse for Block {
        fn parse(input: ParseStream<'_>) -> Result<Self> {
            let content;
            Ok(Self { brace_token: braced!(content in input), stmts: content.parse()? })
        }
    }

    impl Parse for ItemFn {
        fn parse(input: ParseStream<'_>) -> Result<Self> {
            let attrs = input.call(Attribute::parse_outer)?;
            let vis: Visibility = input.parse()?;
            let constness: Option<Token![const]> = input.parse()?;
            let asyncness: Option<Token![async]> = input.parse()?;
            let unsafety: Option<Token![unsafe]> = input.parse()?;
            let abi: Option<Abi> = input.parse()?;
            let fn_token: Token![fn] = input.parse()?;
            let ident: Ident = input.parse()?;
            let generics: Generics = input.parse()?;

            let content;
            let paren_token = parenthesized!(content in input);
            let inputs = content.parse_terminated(FnArg::parse)?;
            let variadic = inputs.last().as_ref().and_then(get_variadic);

            fn get_variadic(input: &&FnArg) -> Option<Variadic> {
                if let FnArg::Typed(PatType { ty, .. }) = input {
                    if let Type::Verbatim(tokens) = &**ty {
                        if let Ok(dots) = parse2(tokens.clone()) {
                            return Some(Variadic { attrs: Vec::new(), dots });
                        }
                    }
                }
                None
            }

            let output: ReturnType = input.parse()?;
            let where_clause: Option<WhereClause> = input.parse()?;

            let block = input.parse()?;

            Ok(Self {
                attrs,
                vis,
                sig: Signature {
                    constness,
                    asyncness,
                    unsafety,
                    abi,
                    fn_token,
                    ident,
                    paren_token,
                    inputs,
                    output,
                    variadic,
                    generics: Generics { where_clause, ..generics },
                },
                block: Box::new(block),
            })
        }
    }
}

mod printing {
    use proc_macro2::TokenStream;
    use quote::{ToTokens, TokenStreamExt};
    use syn::{punctuated::Punctuated, Token, Type};

    use super::{Block, FnArg, ItemFn, Signature};

    impl ToTokens for Block {
        fn to_tokens(&self, tokens: &mut TokenStream) {
            self.brace_token.surround(tokens, |tokens| {
                tokens.append_all(self.stmts.clone());
            });
        }
    }

    fn has_variadic(inputs: &Punctuated<FnArg, Token![,]>) -> bool {
        let last = match inputs.last() {
            Some(last) => last,
            None => return false,
        };

        let pat = match last {
            FnArg::Typed(pat) => pat,
            FnArg::Receiver(_) => return false,
        };

        let tokens = match pat.ty.as_ref() {
            Type::Verbatim(tokens) => tokens,
            _ => return false,
        };

        tokens.to_string() == "..."
    }

    impl ToTokens for Signature {
        fn to_tokens(&self, tokens: &mut TokenStream) {
            self.constness.to_tokens(tokens);
            self.asyncness.to_tokens(tokens);
            self.unsafety.to_tokens(tokens);
            self.abi.to_tokens(tokens);
            self.fn_token.to_tokens(tokens);
            self.ident.to_tokens(tokens);
            self.generics.to_tokens(tokens);
            self.paren_token.surround(tokens, |tokens| {
                self.inputs.to_tokens(tokens);
                if self.variadic.is_some() && !has_variadic(&self.inputs) {
                    if !self.inputs.empty_or_trailing() {
                        <Token![,]>::default().to_tokens(tokens);
                    }
                    self.variadic.to_tokens(tokens);
                }
            });
            self.output.to_tokens(tokens);
            self.generics.where_clause.to_tokens(tokens);
        }
    }

    impl ToTokens for ItemFn {
        fn to_tokens(&self, tokens: &mut TokenStream) {
            tokens.append_all(&self.attrs);
            self.vis.to_tokens(tokens);
            self.sig.to_tokens(tokens);
            self.block.brace_token.surround(tokens, |tokens| {
                tokens.append_all(self.block.stmts.clone());
            });
        }
    }
}
<|endoftext|>


<commit_before>
extern crate ndarray;
pub mod lapack_binding;

use ndarray::prelude::*;
use ndarray::LinalgScalar;
use lapack_binding::Eigh;
use std::error;
use std::fmt;

#[derive(Debug)]
pub struct NotSquareError {
    rows: usize,
    cols: usize,
}

impl fmt::Display for NotSquareError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Not square: rows({}) != cols({})", self.rows, self.cols)
    }
}

impl error::Error for NotSquareError {
    fn description(&self) -> &str {
        "Matrix is not square"
    }
}

#[derive(Debug)]
pub enum LinalgError {
    NotSquare(NotSquareError),
    Lapack(lapack_binding::LapackError),
}

pub trait Matrix: Sized {
    type Vector;
    /// number of rows and cols
    fn size(&self) -> (usize, usize);
    // fn svd(self) -> (Self, Self::Vector, Self);
}

pub trait SquareMatrix: Matrix {
    // fn qr(self) -> (Self, Self);
    // fn lu(self) -> (Self, Self);
    // fn eig(self) -> (Self::Vector, Self);
    /// eigenvalue decomposition for Hermite matrix
    fn eigh(self) -> Result<(Self::Vector, Self), LinalgError>;
    fn check_square(&self) -> Result<(), NotSquareError> {
        let (rows, cols) = self.size();
        if rows == cols {
            Ok(())
        } else {
            Err(NotSquareError {
                rows: rows,
                cols: cols,
            })
        }
    }
}

impl<A> Matrix for Array<A, (Ix, Ix)> {
    type Vector = Array<A, Ix>;
    fn size(&self) -> (usize, usize) {
        (self.rows(), self.cols())
    }
}

impl<A> SquareMatrix for Array<A, (Ix, Ix)>
    where A: Eigh + LinalgScalar
{
    fn eigh(self) -> Result<(Self::Vector, Self), LinalgError> {
        let (rows, cols) = self.size();
        let mut a = self.into_raw_vec();
        let w = match Eigh::syev(rows as i32, &mut a) {
            Ok(w) => w,
            Err(err) => return Err(LinalgError::Lapack(err)),
        };
        let ea = Array::from_vec(w);
        let va = Array::from_vec(a).into_shape((rows, cols)).unwrap().reversed_axes();
        Ok((ea, va))
    }
}
<commit_msg>Use try macro<commit_after>
extern crate ndarray;
pub mod lapack_binding;

use ndarray::prelude::*;
use ndarray::LinalgScalar;
use lapack_binding::Eigh;
use std::error;
use std::fmt;

#[derive(Debug)]
pub struct NotSquareError {
    rows: usize,
    cols: usize,
}

impl fmt::Display for NotSquareError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Not square: rows({}) != cols({})", self.rows, self.cols)
    }
}

impl error::Error for NotSquareError {
    fn description(&self) -> &str {
        "Matrix is not square"
    }
}

#[derive(Debug)]
pub enum LinalgError {
    NotSquare(NotSquareError),
    Lapack(lapack_binding::LapackError),
}

pub trait Matrix: Sized {
    type Vector;
    /// number of rows and cols
    fn size(&self) -> (usize, usize);
    // fn svd(self) -> (Self, Self::Vector, Self);
}

pub trait SquareMatrix: Matrix {
    // fn qr(self) -> (Self, Self);
    // fn lu(self) -> (Self, Self);
    // fn eig(self) -> (Self::Vector, Self);
    /// eigenvalue decomposition for Hermite matrix
    fn eigh(self) -> Result<(Self::Vector, Self), LinalgError>;
    fn check_square(&self) -> Result<(), NotSquareError> {
        let (rows, cols) = self.size();
        if rows == cols {
            Ok(())
        } else {
            Err(NotSquareError {
                rows: rows,
                cols: cols,
            })
        }
    }
}

impl<A> Matrix for Array<A, (Ix, Ix)> {
    type Vector = Array<A, Ix>;
    fn size(&self) -> (usize, usize) {
        (self.rows(), self.cols())
    }
}

impl<A> SquareMatrix for Array<A, (Ix, Ix)>
    where A: Eigh + LinalgScalar
{
    fn eigh(self) -> Result<(Self::Vector, Self), LinalgError> {
        try!(self.check_square().map_err(LinalgError::NotSquare));
        let (rows, cols) = self.size();
        let mut a = self.into_raw_vec();
        let w = try!(Eigh::syev(rows as i32, &mut a).map_err(LinalgError::Lapack));
        let ea = Array::from_vec(w);
        let va = Array::from_vec(a).into_shape((rows, cols)).unwrap().reversed_axes();
        Ok((ea, va))
    }
}
<|endoftext|>


<commit_before>#![crate_name = "argparse"]
#![crate_type = "lib"]
#![feature(box_syntax)]

extern crate collections;
extern crate arena;

pub use self::parser::{ArgumentParser, Ref};

pub mod action;
pub mod parser;
mod generic;
mod help;

mod bool;
mod num;

pub struct StoreTrue;

pub struct StoreFalse;

pub struct StoreConst<T>(pub T);

pub struct Store<T>;

pub struct StoreOption<T>;

pub struct List<T>;

pub struct Collect<T>;

pub struct IncrBy<T>(pub T);

pub struct DecrBy<T>(pub T);

pub type StoreBool = Store<bool>;

#[cfg(test)] mod test_parser;
#[cfg(test)] mod test_bool;
#[cfg(test)] mod test_int;
#[cfg(test)] mod test_float;
#[cfg(test)] mod test_str;
#[cfg(test)] mod test_enum;
#[cfg(test)] mod test_pos;
#[cfg(test)] mod test_many;
#[cfg(test)] mod test_optional;
#[cfg(test)] mod test_usage;
#[cfg(test)] mod test_help;
#[cfg(test)] mod test_env;
<commit_msg>Removed unneeded `arena` dependency<commit_after>#![crate_name = "argparse"]
#![crate_type = "lib"]
#![feature(box_syntax)]

extern crate collections;

pub use self::parser::{ArgumentParser, Ref};

pub mod action;
pub mod parser;
mod generic;
mod help;

mod bool;
mod num;

pub struct StoreTrue;

pub struct StoreFalse;

pub struct StoreConst<T>(pub T);

pub struct Store<T>;

pub struct StoreOption<T>;

pub struct List<T>;

pub struct Collect<T>;

pub struct IncrBy<T>(pub T);

pub struct DecrBy<T>(pub T);

pub type StoreBool = Store<bool>;

#[cfg(test)] mod test_parser;
#[cfg(test)] mod test_bool;
#[cfg(test)] mod test_int;
#[cfg(test)] mod test_float;
#[cfg(test)] mod test_str;
#[cfg(test)] mod test_enum;
#[cfg(test)] mod test_pos;
#[cfg(test)] mod test_many;
#[cfg(test)] mod test_optional;
#[cfg(test)] mod test_usage;
#[cfg(test)] mod test_help;
#[cfg(test)] mod test_env;
<|endoftext|>


<commit_before>#[macro_use]
extern crate serde_json;
extern crate hyper;
extern crate hyper_openssl;
use std::io::Read;
use std::{panic, thread, fmt};
use std::borrow::ToOwned;
use std::sync::Arc;

pub enum Level {
    CRITICAL,
    ERROR,
    WARNING,
    INFO,
    DEBUG
}

impl<'a> From<&'a str> for Level {
    fn from(s: &'a str) -> Level {
        match s {
            "critical" => Level::CRITICAL,
            "warning"  => Level::WARNING,
            "info"     => Level::INFO,
            "debug"    => Level::DEBUG,
            _          => Level::ERROR
        }
    }
}

impl ToString for Level {
    fn to_string(&self) -> String {
        match self {
            &Level::CRITICAL => "critical".to_string(),
            &Level::ERROR    => "error".to_string(),
            &Level::WARNING  => "warning".to_string(),
            &Level::INFO     => "info".to_string(),
            &Level::DEBUG    => "debug".to_string()
        }
    }
}

#[derive(Default)]
pub struct Error {
    filename: String,
    line_number: u32,
    class: String,
    message: String,
    description: String
}

impl<T: fmt::Debug> From<T> for Error {
    fn from(error: T) -> Error {
        let error_message = format!("{:?}", error);
        Error {
            message: error_message.to_owned(),
            description: error_message,
            ..Default::default()
        }
    }
}

impl Error {
    pub fn from_panic(panic_info: &panic::PanicInfo) -> Error {
        let payload = panic_info.payload();
        let error_message = match payload.downcast_ref::<&str>() {
            Some(s) => *s,
            None => match payload.downcast_ref::<String>() {
                Some(s) => &s[..],
                None => "Box<Any>"
            }
        };

        match panic_info.location() {
            Some(location) => {
                Error {
                    filename: location.file().to_owned(),
                    line_number: location.line().to_owned(),
                    class: String::new(),
                    message: error_message.to_string(),
                    description: error_message.to_string()
                }
            },
            None => {
                Error {
                    message: error_message.to_string(),
                    description: error_message.to_string(),
                    ..Default::default()
                }
            }
        }
    }
}

pub trait ErrorToPayload {
    fn build_payload(&self, report_builder: &ReportBuilder) -> String;
}

pub trait MessageToPayload {
    fn build_payload(&self, report_builder: &ReportBuilder) -> String;
}

impl ErrorToPayload for Error {
    fn build_payload(&self, report_builder: &ReportBuilder) -> String {
        let client = report_builder.client;
        json!({
            "access_token": client.access_token,
            "data": {
                "environment": client.environment,
                "body": {
                    "trace": {
                        "frames": [{
                            "filename": self.filename,
                            "lineno": self.line_number
                        }],
                        "exception": {
                            "class": self.class,
                            "message": self.message,
                            "description": self.description
                        }
                    }
                },
                "level": match report_builder.level {
                    Some(ref level) => level.to_string(),
                    None => Level::ERROR.to_string()
                },
                "language": "rust"
            }
        }).to_string()
    }
}

impl<'a> MessageToPayload for &'a str {
    fn build_payload(&self, report_builder: &ReportBuilder) -> String {
        let client = report_builder.client;
        json!({
            "access_token": client.access_token,
            "data": {
                "body": {
                    "environment": client.environment,
                    "message": {
                        "body": self
                    }
                },
                "level": match report_builder.level {
                    Some(ref level) => level.to_string(),
                    None => Level::ERROR.to_string()
                }
            }
        }).to_string()
    }
}

// https://rollbar.com/docs/api/items_post/
const URL: &'static str = "https://api.rollbar.com/api/1/item/";

pub struct ReportBuilder<'a> {
    client: &'a Client<'a>,
    level: Option<Level>,
    send_strategy: Option<Box<Fn(Arc<hyper::Client>, String)>>
}

impl<'a> ReportBuilder<'a> {
    pub fn report<T>(&mut self, error: T) -> &mut Self where T: Into<Error> {
        match self.send_strategy {
            Some(ref send_strategy) => {
                let http_client = self.client.http_client.clone();
                let payload = error.into().build_payload(&self);
                send_strategy(http_client, payload);
            },
            None => {
                let payload = error.into().build_payload(&self);
                self.client.send(payload);
            }
        };
        self
    }

    pub fn with_level<T>(&'a mut self, level: T) -> &'a mut Self where T: Into<Level> {
        self.level = Some(level.into());
        self
    }

    pub fn with_send_strategy(&'a mut self, send_strategy: Box<Fn(Arc<hyper::Client>, String)>) -> &'a mut Self {
        self.send_strategy = Some(send_strategy);
        self
    }
}

pub struct Client<'a> {
    http_client: Arc<hyper::Client>,
    access_token: &'a str,
    environment: &'a str
}

impl<'a> Client<'a> {
    pub fn new(access_token: &'a str, environment: &'a str) -> Client<'a> {
        let ssl = hyper_openssl::OpensslClient::new().unwrap();
        let connector = hyper::net::HttpsConnector::new(ssl);

        Client {
            http_client: Arc::new(hyper::Client::with_connector(connector)),
            access_token: access_token,
            environment: environment
        }
    }

    pub fn build_report(&self) -> ReportBuilder {
        ReportBuilder {
            client: self,
            level: None,
            send_strategy: None
        }
    }

    pub fn send(&self, payload: String) {
        let http_client = self.http_client.clone();

        let _ = thread::spawn(move || {
            let res = http_client.post(URL).body(&*payload).send();

            match res {
                Ok(mut res) => {
                    let mut body = String::new();
                    res.read_to_string(&mut body).unwrap();

                    println!("- Error while sending a report to Rollbar.");
                    println!("\n- The error that Rollbar raised was:\n{:?}", res);
                    println!("\n- The message that Rollbar returned was:\n{}", body);
                    println!("\n- The error that your application raised was:\n{}", payload);
                },
                Err(e) => {
                    println!("- Error while sending a report to Rollbar.");
                    println!("\n- The error that Rollbar raised was:\n{:?}", e);
                    println!("\n- The error that your application raised was:\n{}", payload);
                }
            }
        }).join();
    }
}

#[cfg(test)]
mod tests {
    extern crate serde_json;
    extern crate hyper;

    use std::panic;
    use super::{Client, Level, Error, MessageToPayload, ErrorToPayload};
    use std::sync::{Arc, Mutex};
    use std::sync::mpsc::channel;

    // TODO: rewrite this shit
    #[test]
    fn test_build_payload_from_panic() {
        let (tx, rx) = channel();

        {
            let tx = Arc::new(Mutex::new(tx));

            let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");
            panic::set_hook(Box::new(move |panic_info| {
                let error = Error::from_panic(panic_info).build_payload(
                    client.build_report().with_level("info"));
                let error = Arc::new(Mutex::new(error));
                tx.lock().unwrap().send(error).unwrap();
            }));

            let result = panic::catch_unwind(|| {
                // just to trick the linter
                let zero = "0".parse::<i32>().unwrap();
                let _ = 1/zero;
            });
            assert!(result.is_err());
        }

        // remove the hook to avoid double panics
        let _ = panic::take_hook();

        let lock = rx.recv().unwrap();
        let error = match lock.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let     payload:          serde_json::Value = serde_json::from_str(&*error).unwrap();
        let mut expected_payload: serde_json::Value = serde_json::from_str(
            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"trace":{"exception":{"class":"","description":"attempt to divide by zero","message":"attempt to divide by zero"},"frames":[{"filename":"src/lib.rs","lineno":268}]}},"environment":"ENVIRONMENT","level":"info","language":"rust"}}"#
        ).unwrap();

        *expected_payload.get_mut("data").unwrap()
            .get_mut("body").unwrap()
            .get_mut("trace").unwrap()
            .get_mut("frames").unwrap() = payload.get("data").unwrap()
                                            .get("body").unwrap()
                                            .get("trace").unwrap()
                                            .get("frames").unwrap()
                                            .clone();

        assert_eq!(expected_payload.to_string(), payload.to_string());
    }

    #[test]
    fn test_report_match() {
        let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");

        match "笑".parse::<i32>() {
            Ok(_) => { println!("lolnope"); },
            Err(e) => {
                client.build_report()
                    .with_level(Level::ERROR)
                    .with_send_strategy(Box::new(|_, payload| {
                        assert_eq!(
                            payload,
                            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"trace":{"exception":{"class":"","description":"ParseIntError { kind: InvalidDigit }","message":"ParseIntError { kind: InvalidDigit }"},"frames":[{"filename":"","lineno":0}]}},"environment":"ENVIRONMENT","language":"rust","level":"error"}}"#
                        );
                    }))
                    .report(e);
            }
        }
    }

    #[test]
    fn test_payload_string() {
        let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");
        let report = client.build_report();
        assert_eq!(
            "hai".build_payload(&report),
            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"environment":"ENVIRONMENT","message":{"body":"hai"}},"level":"error"}}"#
        );
    }
}
<commit_msg>lib: store Client data as String instead of &str<commit_after>#[macro_use]
extern crate serde_json;
extern crate hyper;
extern crate hyper_openssl;
use std::io::Read;
use std::{panic, thread, fmt};
use std::borrow::ToOwned;
use std::sync::Arc;

pub enum Level {
    CRITICAL,
    ERROR,
    WARNING,
    INFO,
    DEBUG
}

impl<'a> From<&'a str> for Level {
    fn from(s: &'a str) -> Level {
        match s {
            "critical" => Level::CRITICAL,
            "warning"  => Level::WARNING,
            "info"     => Level::INFO,
            "debug"    => Level::DEBUG,
            _          => Level::ERROR
        }
    }
}

impl ToString for Level {
    fn to_string(&self) -> String {
        match self {
            &Level::CRITICAL => "critical".to_string(),
            &Level::ERROR    => "error".to_string(),
            &Level::WARNING  => "warning".to_string(),
            &Level::INFO     => "info".to_string(),
            &Level::DEBUG    => "debug".to_string()
        }
    }
}

#[derive(Default)]
pub struct Error {
    filename: String,
    line_number: u32,
    class: String,
    message: String,
    description: String
}

impl<T: fmt::Debug> From<T> for Error {
    fn from(error: T) -> Error {
        let error_message = format!("{:?}", error);
        Error {
            message: error_message.to_owned(),
            description: error_message,
            ..Default::default()
        }
    }
}

impl Error {
    pub fn from_panic(panic_info: &panic::PanicInfo) -> Error {
        let payload = panic_info.payload();
        let error_message = match payload.downcast_ref::<&str>() {
            Some(s) => *s,
            None => match payload.downcast_ref::<String>() {
                Some(s) => &s[..],
                None => "Box<Any>"
            }
        };

        match panic_info.location() {
            Some(location) => {
                Error {
                    filename: location.file().to_owned(),
                    line_number: location.line().to_owned(),
                    class: String::new(),
                    message: error_message.to_string(),
                    description: error_message.to_string()
                }
            },
            None => {
                Error {
                    message: error_message.to_string(),
                    description: error_message.to_string(),
                    ..Default::default()
                }
            }
        }
    }
}

pub trait ErrorToPayload {
    fn build_payload(&self, report_builder: &ReportBuilder) -> String;
}

pub trait MessageToPayload {
    fn build_payload(&self, report_builder: &ReportBuilder) -> String;
}

impl ErrorToPayload for Error {
    fn build_payload(&self, report_builder: &ReportBuilder) -> String {
        let client = report_builder.client;
        json!({
            "access_token": client.access_token,
            "data": {
                "environment": client.environment,
                "body": {
                    "trace": {
                        "frames": [{
                            "filename": self.filename,
                            "lineno": self.line_number
                        }],
                        "exception": {
                            "class": self.class,
                            "message": self.message,
                            "description": self.description
                        }
                    }
                },
                "level": match report_builder.level {
                    Some(ref level) => level.to_string(),
                    None => Level::ERROR.to_string()
                },
                "language": "rust"
            }
        }).to_string()
    }
}

impl<'a> MessageToPayload for &'a str {
    fn build_payload(&self, report_builder: &ReportBuilder) -> String {
        let client = report_builder.client;
        json!({
            "access_token": client.access_token,
            "data": {
                "body": {
                    "environment": client.environment,
                    "message": {
                        "body": self
                    }
                },
                "level": match report_builder.level {
                    Some(ref level) => level.to_string(),
                    None => Level::ERROR.to_string()
                }
            }
        }).to_string()
    }
}

// https://rollbar.com/docs/api/items_post/
const URL: &'static str = "https://api.rollbar.com/api/1/item/";

pub struct ReportBuilder<'a> {
    client: &'a Client,
    level: Option<Level>,
    send_strategy: Option<Box<Fn(Arc<hyper::Client>, String)>>
}

impl<'a> ReportBuilder<'a> {
    pub fn report<T>(&mut self, error: T) -> &mut Self where T: Into<Error> {
        match self.send_strategy {
            Some(ref send_strategy) => {
                let http_client = self.client.http_client.clone();
                let payload = error.into().build_payload(&self);
                send_strategy(http_client, payload);
            },
            None => {
                let payload = error.into().build_payload(&self);
                self.client.send(payload);
            }
        };
        self
    }

    pub fn with_level<T>(&'a mut self, level: T) -> &'a mut Self where T: Into<Level> {
        self.level = Some(level.into());
        self
    }

    pub fn with_send_strategy(&'a mut self, send_strategy: Box<Fn(Arc<hyper::Client>, String)>) -> &'a mut Self {
        self.send_strategy = Some(send_strategy);
        self
    }
}

pub struct Client {
    http_client: Arc<hyper::Client>,
    access_token: String,
    environment: String
}

impl Client {
    pub fn new<T: Into<String>>(access_token: T, environment: T) -> Client {
        let ssl = hyper_openssl::OpensslClient::new().unwrap();
        let connector = hyper::net::HttpsConnector::new(ssl);

        Client {
            http_client: Arc::new(hyper::Client::with_connector(connector)),
            access_token: access_token.into(),
            environment: environment.into()
        }
    }

    pub fn build_report(&self) -> ReportBuilder {
        ReportBuilder {
            client: self,
            level: None,
            send_strategy: None
        }
    }

    pub fn send(&self, payload: String) {
        let http_client = self.http_client.clone();

        let _ = thread::spawn(move || {
            let res = http_client.post(URL).body(&*payload).send();

            match res {
                Ok(mut res) => {
                    let mut body = String::new();
                    res.read_to_string(&mut body).unwrap();

                    println!("- Error while sending a report to Rollbar.");
                    println!("\n- The error that Rollbar raised was:\n{:?}", res);
                    println!("\n- The message that Rollbar returned was:\n{}", body);
                    println!("\n- The error that your application raised was:\n{}", payload);
                },
                Err(e) => {
                    println!("- Error while sending a report to Rollbar.");
                    println!("\n- The error that Rollbar raised was:\n{:?}", e);
                    println!("\n- The error that your application raised was:\n{}", payload);
                }
            }
        }).join();
    }
}

#[cfg(test)]
mod tests {
    extern crate serde_json;
    extern crate hyper;

    use std::panic;
    use super::{Client, Level, Error, MessageToPayload, ErrorToPayload};
    use std::sync::{Arc, Mutex};
    use std::sync::mpsc::channel;

    // TODO: rewrite this shit
    #[test]
    fn test_build_payload_from_panic() {
        let (tx, rx) = channel();

        {
            let tx = Arc::new(Mutex::new(tx));

            let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");
            panic::set_hook(Box::new(move |panic_info| {
                let error = Error::from_panic(panic_info).build_payload(
                    client.build_report().with_level("info"));
                let error = Arc::new(Mutex::new(error));
                tx.lock().unwrap().send(error).unwrap();
            }));

            let result = panic::catch_unwind(|| {
                // just to trick the linter
                let zero = "0".parse::<i32>().unwrap();
                let _ = 1/zero;
            });
            assert!(result.is_err());
        }

        // remove the hook to avoid double panics
        let _ = panic::take_hook();

        let lock = rx.recv().unwrap();
        let error = match lock.lock() {
            Ok(guard) => guard,
            Err(poisoned) => poisoned.into_inner(),
        };

        let     payload:          serde_json::Value = serde_json::from_str(&*error).unwrap();
        let mut expected_payload: serde_json::Value = serde_json::from_str(
            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"trace":{"exception":{"class":"","description":"attempt to divide by zero","message":"attempt to divide by zero"},"frames":[{"filename":"src/lib.rs","lineno":268}]}},"environment":"ENVIRONMENT","level":"info","language":"rust"}}"#
        ).unwrap();

        *expected_payload.get_mut("data").unwrap()
            .get_mut("body").unwrap()
            .get_mut("trace").unwrap()
            .get_mut("frames").unwrap() = payload.get("data").unwrap()
                                            .get("body").unwrap()
                                            .get("trace").unwrap()
                                            .get("frames").unwrap()
                                            .clone();

        assert_eq!(expected_payload.to_string(), payload.to_string());
    }

    #[test]
    fn test_report_match() {
        let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");

        match "笑".parse::<i32>() {
            Ok(_) => { println!("lolnope"); },
            Err(e) => {
                client.build_report()
                    .with_level(Level::ERROR)
                    .with_send_strategy(Box::new(|_, payload| {
                        assert_eq!(
                            payload,
                            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"trace":{"exception":{"class":"","description":"ParseIntError { kind: InvalidDigit }","message":"ParseIntError { kind: InvalidDigit }"},"frames":[{"filename":"","lineno":0}]}},"environment":"ENVIRONMENT","language":"rust","level":"error"}}"#
                        );
                    }))
                    .report(e);
            }
        }
    }

    #[test]
    fn test_payload_string() {
        let client = Client::new("ACCESS_TOKEN", "ENVIRONMENT");
        let report = client.build_report();
        assert_eq!(
            "hai".build_payload(&report),
            r#"{"access_token":"ACCESS_TOKEN","data":{"body":{"environment":"ENVIRONMENT","message":{"body":"hai"}},"level":"error"}}"#
        );
    }
}
<|endoftext|>


<commit_before>#![warn(bad_style, missing_docs,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]

#![feature(into_cow)]

extern crate oauth_client as oauth;
extern crate rustc_serialize as rustc_serialize;

use std::borrow::{Cow, IntoCow};
use std::collections::HashMap;
use oauth::Token;
use rustc_serialize::Decodable;
use rustc_serialize::json::{self, Json};

mod api_twitter_oauth {
    pub const REQUEST_TOKEN: &'static str   = "https://api.twitter.com/oauth/request_token";
    pub const AUTHORIZE: &'static str       = "https://api.twitter.com/oauth/authorize";
    pub const ACCESS_TOKEN: &'static str    = "https://api.twitter.com/oauth/access_token";
}

mod api_twitter_soft {
    pub const UPDATE_STATUS: &'static str = "https://api.twitter.com/1.1/statuses/update.json";
    pub const HOME_TIMELINE: &'static str = "https://api.twitter.com/1.1/statuses/home_timeline.json";
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Tweet {
    pub created_at: String,
    pub text: String
}

impl Tweet {
    pub fn parse_timeline(json_string: String) -> Option<Tweet> {
        let conf = Json::from_str(&json_string).unwrap();
        let d = Decodable::decode(&mut json::Decoder::new(conf));
        println!("{:?}", d);
        d.ok()
    }
}

fn split_query<'a>(query: &'a str) -> HashMap<Cow<'a, str>, Cow<'a, str>> {
    let mut param = HashMap::new();
    for q in query.split('&') {
        let mut s = q.splitn(2, '=');
        let k = s.next().unwrap();
        let v = s.next().unwrap();
        let _ = param.insert(k.into_cow(), v.into_cow());
    }
    param
}

pub fn get_request_token(consumer: &Token) -> Token<'static> {
    let resp = oauth::get(api_twitter_oauth::REQUEST_TOKEN, consumer, None, None);
    let param = split_query(&resp);
    Token::new(param.get("oauth_token").unwrap().to_string(),
               param.get("oauth_token_secret").unwrap().to_string())
}

pub fn get_authorize_url(request: &Token) -> String {
    format!("{}?oauth_token={}", api_twitter_oauth::AUTHORIZE, request.key)
}

pub fn get_access_token(consumer: &Token, request: &Token, pin: &str) -> Token<'static> {
    let mut param = HashMap::new();
    let _ = param.insert("oauth_verifier".into_cow(), pin.into_cow());
    let resp = oauth::get(api_twitter_oauth::ACCESS_TOKEN, consumer, Some(request), Some(&param));
    let param = split_query(&resp);
    Token::new(param.get("oauth_token").unwrap().to_string(),
               param.get("oauth_token_secret").unwrap().to_string())
}

/// function to update the status
/// This function takes as arguments the consumer key, the access key, and the status (obviously)
pub fn update_status(consumer: &Token, access: &Token, status: &str) {
    let mut param = HashMap::new();
    let _ = param.insert("status".into_cow(), status.into_cow());
    let _ = oauth::post(api_twitter_soft::UPDATE_STATUS, consumer, Some(access), Some(&param));
}

pub fn get_last_tweets(consumer: &Token, access: &Token) {
    let mut param = HashMap::new();
    println!("{:?}", oauth::get(api_twitter_soft::HOME_TIMELINE, consumer, Some(access), Some(&param)));
}
<commit_msg>src/lib.rs : get last tweets<commit_after>#![warn(bad_style, missing_docs,
        unused, unused_extern_crates, unused_import_braces,
        unused_qualifications, unused_results)]

#![feature(into_cow)]

extern crate oauth_client as oauth;
extern crate rustc_serialize as rustc_serialize;

use std::borrow::{Cow, IntoCow};
use std::collections::HashMap;
use oauth::Token;
use rustc_serialize::Decodable;
use rustc_serialize::json::{self, Json};

mod api_twitter_oauth {
    pub const REQUEST_TOKEN: &'static str   = "https://api.twitter.com/oauth/request_token";
    pub const AUTHORIZE: &'static str       = "https://api.twitter.com/oauth/authorize";
    pub const ACCESS_TOKEN: &'static str    = "https://api.twitter.com/oauth/access_token";
}

mod api_twitter_soft {
    pub const UPDATE_STATUS: &'static str = "https://api.twitter.com/1.1/statuses/update.json";
    pub const HOME_TIMELINE: &'static str = "https://api.twitter.com/1.1/statuses/home_timeline.json";
}

#[derive(Debug, RustcEncodable, RustcDecodable)]
pub struct Tweet {
    pub created_at: String,
    pub text: String
}

impl Tweet {
    pub fn parse_timeline(json_string: String) -> Option<Tweet> {
        let conf = Json::from_str(&json_string).unwrap();
        let d = Decodable::decode(&mut json::Decoder::new(conf));
        println!("{:?}", d);
        d.ok()
    }
}

fn split_query<'a>(query: &'a str) -> HashMap<Cow<'a, str>, Cow<'a, str>> {
    let mut param = HashMap::new();
    for q in query.split('&') {
        let mut s = q.splitn(2, '=');
        let k = s.next().unwrap();
        let v = s.next().unwrap();
        let _ = param.insert(k.into_cow(), v.into_cow());
    }
    param
}

pub fn get_request_token(consumer: &Token) -> Token<'static> {
    let resp = oauth::get(api_twitter_oauth::REQUEST_TOKEN, consumer, None, None);
    let param = split_query(&resp);
    Token::new(param.get("oauth_token").unwrap().to_string(),
               param.get("oauth_token_secret").unwrap().to_string())
}

pub fn get_authorize_url(request: &Token) -> String {
    format!("{}?oauth_token={}", api_twitter_oauth::AUTHORIZE, request.key)
}

pub fn get_access_token(consumer: &Token, request: &Token, pin: &str) -> Token<'static> {
    let mut param = HashMap::new();
    let _ = param.insert("oauth_verifier".into_cow(), pin.into_cow());
    let resp = oauth::get(api_twitter_oauth::ACCESS_TOKEN, consumer, Some(request), Some(&param));
    let param = split_query(&resp);
    Token::new(param.get("oauth_token").unwrap().to_string(),
               param.get("oauth_token_secret").unwrap().to_string())
}

/// function to update the status
/// This function takes as arguments the consumer key, the access key, and the status (obviously)
pub fn update_status(consumer: &Token, access: &Token, status: &str) {
    let mut param = HashMap::new();
    let _ = param.insert("status".into_cow(), status.into_cow());
    let _ = oauth::post(api_twitter_soft::UPDATE_STATUS, consumer, Some(access), Some(&param));
}

pub fn get_last_tweets(consumer: &Token, access: &Token){
    let mut param = HashMap::new();
    let last_tweets_json = oauth::get(api_twitter_soft::HOME_TIMELINE, consumer, Some(access), Some(&param));
    match Tweet::parse_timeline(last_tweets_json) {
        Some(t) => println!("{} - {}", t.created_at, t.text),
        None => println!("No tweet in your timeline...")
    }
}
<|endoftext|>


<commit_before>extern crate rand;
extern crate rustc_serialize;
extern crate time;
extern crate tiny_http;
extern crate url;

pub use assets::match_assets;
pub use log::LogEntry;
pub use input::{SessionsManager, Session, generate_session_id};

use std::io;
use std::io::Cursor;
use std::io::Read;
use std::error;
use std::fmt;
use std::fs::File;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::thread;

pub mod input;

mod assets;
mod find_route;
mod log;
mod router;

/// An error that one of your routes can return.
///
/// This is just a convenience enum and you don't need to use it in your project
/// if you don't want to.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RouteError {
    /// Couldn't find a way to handle this request.
    NoRouteFound,

    /// The user input is wrong.
    WrongInput,

    /// The user must be logged in.
    LoginRequired,

    /// The user entered a wrong login or password.
    WrongLoginPassword,

    /// The user is logged in but shouldn't be there.
    NotAuthorized,
}

impl error::Error for RouteError {
    fn description(&self) -> &str {
        match self {
            &RouteError::NoRouteFound => {
                "Couldn't find a way to handle this request."
            },
            &RouteError::WrongInput => {
                "The body of the request is malformed or missing something."
            },
            &RouteError::LoginRequired => {
                "The client must be logged in before this request can be answered."
            },
            &RouteError::WrongLoginPassword => {
                "The client attempted to login but entered a wrong login or password."
            },
            &RouteError::NotAuthorized => {
                "The client is logged in but doesn't have the permission to access this resource."
            },
        }
    }
}

impl fmt::Display for RouteError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

/// Starts a server and uses the given requests handler.
///
/// The request handler takes a `&Request` and must return a `Response` to send to the user.
///
/// # Common mistakes
///
/// The handler must capture its environment by value and not by reference (`'static`). If you
/// use closure, don't forget to put `move` in front of the closure.
///
/// The handler must also be thread-safe (`Send` and `Sync`).
/// For example this handler isn't thread-safe:
///
/// ```ignore
/// let mut requests_counter = 0;
///
/// rouille::start_server("localhost:80", move |request| {
///     requests_counter += 1;
///
///     // rest of the handler
/// })
/// ```
///
/// Multiple requests can be processed simultaneously, therefore you can't mutably access
/// variables from the outside.
///
/// Instead you must use a `Mutex`:
///
/// ```no_run
/// use std::sync::Mutex;
/// let requests_counter = Mutex::new(0);
///
/// rouille::start_server("localhost:80", move |request| {
///     *requests_counter.lock().unwrap() += 1;
///
///     // rest of the handler
/// # panic!()
/// })
/// ```
///
/// # Panic handling
///
/// If your request handler panicks, a 500 error will automatically be sent to the client.
///
pub fn start_server<A, F>(addr: A, handler: F) -> !
                          where A: ToSocketAddrs,
                                F: Send + Sync + 'static + Fn(&Request) -> Response
{
    let server = tiny_http::ServerBuilder::new().with_port(8000).build().unwrap();

    for mut request in server.incoming_requests() {
        // TODO: don't read the body in memory immediately
        let mut data = Vec::with_capacity(request.body_length().unwrap_or(0));
        request.as_reader().read_to_end(&mut data);     // TODO: handle error

        // building the `Request` object
        let rouille_request = Request {
            url: request.url().to_owned(),
            method: request.method().as_str().to_owned(),
            headers: request.headers().iter().map(|h| (h.field.to_string(), h.value.clone().into())).collect(),
            https: false,
            data: data,
            remote_addr: request.remote_addr().clone(),
        };

        // calling the handler ; this most likely takes a lot of time
        let mut rouille_response = handler(&rouille_request);

        // writing the response
        let mut response = tiny_http::Response::empty(rouille_response.status_code)
                    .with_data(rouille_response.data.data, rouille_response.data.data_length);

        for (key, value) in rouille_response.headers {
            if let Ok(header) = tiny_http::Header::from_bytes(key, value) {
                response.add_header(header);
            } else {
                // TODO: ?
            }
        }

        request.respond(response);
    }

    unreachable!()
}

/// Represents a request that your handler must answer to.
///
/// This can be either a real request (received by the HTTP server) or a mock object created with
/// one of the `fake_*` constructors.
pub struct Request {
    method: String,
    url: String,
    headers: Vec<(String, String)>,
    https: bool,
    data: Vec<u8>,
    remote_addr: SocketAddr,
}

impl Request {
    /// Builds a fake HTTP request to be used during tests.
    ///
    /// The remote address of the client will be `127.0.0.1:12345`. Use `fake_http_from` to
    /// specify what the client's address should be.
    pub fn fake_http<U, M>(method: M, url: U, headers: Vec<(String, String)>, data: Vec<u8>)
                           -> Request where U: Into<String>, M: Into<String>
    {
        Request {
            url: url.into(),
            method: method.into(),
            https: false,
            data: data,
            headers: headers,
            remote_addr: "127.0.0.1:12345".parse().unwrap(),
        }
    }

    /// Builds a fake HTTP request to be used during tests.
    pub fn fake_http_from<U, M>(from: SocketAddr, method: M, url: U,
                                headers: Vec<(String, String)>, data: Vec<u8>)
                                -> Request where U: Into<String>, M: Into<String>
    {
        Request {
            url: url.into(),
            method: method.into(),
            https: false,
            data: data,
            headers: headers,
            remote_addr: from,
        }
    }

    /// Builds a fake HTTPS request to be used during tests.
    ///
    /// The remote address of the client will be `127.0.0.1:12345`. Use `fake_https_from` to
    /// specify what the client's address should be.
    pub fn fake_https<U, M>(method: M, url: U, headers: Vec<(String, String)>, data: Vec<u8>)
                            -> Request where U: Into<String>, M: Into<String>
    {
        Request {
            url: url.into(),
            method: method.into(),
            https: true,
            data: data,
            headers: headers,
            remote_addr: "127.0.0.1:12345".parse().unwrap(),
        }
    }

    /// Builds a fake HTTPS request to be used during tests.
    pub fn fake_https_from<U, M>(from: SocketAddr, method: M, url: U,
                                 headers: Vec<(String, String)>, data: Vec<u8>)
                                 -> Request where U: Into<String>, M: Into<String>
    {
        Request {
            url: url.into(),
            method: method.into(),
            https: true,
            data: data,
            headers: headers,
            remote_addr: from,
        }
    }

    /// Returns `true` if the request uses HTTPS instead of HTTP.
    #[inline]
    pub fn secure(&self) -> bool {
        self.https
    }

    /// Returns the method of the request (`GET`, `POST`, etc.).
    #[inline]
    pub fn method(&self) -> &str {
        &self.method
    }

    /// Returns the URL requested by the client. It is not decoded and thus can contain strings
    /// such as `%20`.
    ///
    /// See also `url()`.
    #[inline]
    pub fn raw_url(&self) -> &str {
        &self.url
    }

    /// Returns the URL requested by the client.
    ///
    /// Contrary to `raw_url`, special characters have been decoded.
    /// If there is any non-unicode character in the URL, it will be replaced with `U+FFFD`.
    pub fn url(&self) -> String {
        url::percent_encoding::lossy_utf8_percent_decode(self.url.as_bytes())
    }

    /// Returns the value of a header of the request.
    ///
    /// Returns `None` if no such header could be found.
    #[inline]
    pub fn header(&self, key: &str) -> Option<String> {
        self.headers.iter().find(|&&(ref k, _)| k == key).map(|&(_, ref v)| v.clone())
    }

    /// UNSTABLE. Returns the body of the request.
    ///
    /// Will eventually return an object that implements `Read` instead of a `Vec<u8>`.
    pub fn data(&self) -> Vec<u8> {
        self.data.clone()
    }

    /// Returns the address of the client that made this request.
    #[inline]
    pub fn remote_addr(&self) -> &SocketAddr {
        &self.remote_addr
    }
}

/// Contains a prototype of a response.
/// The response is only sent when you call `Request::respond`.
pub struct Response {
    /// The status code to return to the user.
    pub status_code: u16,

    /// List of headers to be returned in the response.
    ///
    /// Note that important headers such as `Connection` or `Content-Length` will be ignored
    /// from this list.
    // TODO: document precisely which headers
    pub headers: Vec<(String, String)>,

    /// An opaque type that contains the body of the response.
    pub data: ResponseBody,
}

impl Response {
    /// UNSTABLE. Builds a default response to handle the given route error.
    ///
    /// Important: don't use this in a real website. This function is just a convenience when
    /// prototyping.
    ///
    /// For authentication-related errors, you are strongly encouraged to handle them yourself.
    #[inline]
    pub fn from_error(err: &RouteError) -> Response {
        match err {
            &RouteError::NoRouteFound => Response {
                status_code: 404, headers: vec![], data: ResponseBody::empty()
            },
            &RouteError::WrongInput => Response {
                status_code: 400, headers: vec![], data: ResponseBody::empty()
            },
            &RouteError::LoginRequired => Response {
                status_code: 401, headers: vec![], data: ResponseBody::empty()
            },     // TODO: www-auth header?
            &RouteError::WrongLoginPassword => Response {
                status_code: 401, headers: vec![], data: ResponseBody::empty()
            },     // TODO: www-auth header?
            &RouteError::NotAuthorized => Response {
                status_code: 403, headers: vec![], data: ResponseBody::empty()
            },
        }
    }

    /// Builds a `Response` that redirects the user to another URL.
    #[inline]
    pub fn redirect(target: &str) -> Response {
        Response {
            status_code: 303,
            headers: vec![("Location".to_owned(), target.to_owned())],
            data: ResponseBody::empty(),
        }
    }

    /// Builds a `Response` that outputs HTML.
    #[inline]
    pub fn html<D>(content: D) -> Response where D: Into<Vec<u8>> {
        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "text/html; charset=utf8".to_owned())],
            data: ResponseBody::from_data(content),
        }
    }

    /// Builds a `Response` that outputs plain text.
    #[inline]
    pub fn text<S>(text: S) -> Response where S: Into<String> {
        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "text/plain; charset=utf8".to_owned())],
            data: ResponseBody::from_string(text),
        }
    }

    /// Builds a `Response` that outputs JSON.
    #[inline]
    pub fn json<T>(content: &T) -> Response where T: rustc_serialize::Encodable {
        let data = rustc_serialize::json::encode(content).unwrap();

        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "application/json".to_owned())],
            data: ResponseBody::from_data(data),
        }
    }

    /// Builds a `Response` that returns a `401 Not Authorized` status
    /// and a `WWW-Authenticate` header.
    #[inline]
    pub fn basic_http_auth_login_required(realm: &str) -> Response {
        // TODO: escape the realm
        Response {
            status_code: 401,
            headers: vec![("WWW-Authenticate".to_owned(), format!("Basic realm=\"{}\"", realm))],
            data: ResponseBody::empty(),
        }
    }

    /// Changes the status code of the response.
    #[inline]
    pub fn with_status_code(mut self, code: u16) -> Response {
        self.status_code = code;
        self
    }
}

/// An opaque type that represents the body of a response.
pub struct ResponseBody {
    data: Box<Read + Send>,
    data_length: Option<usize>,
}

impl ResponseBody {
    /// Builds a `ResponseBody` that doesn't return any data.
    #[inline]
    pub fn empty() -> ResponseBody {
        ResponseBody {
            data: Box::new(io::empty()),
            data_length: Some(0),
        }
    }

    /// Builds a new `ResponseBody` that will read the data from a `Read`.
    ///
    /// Note that this is suboptimal compared to other constructors because the length
    /// isn't known in advance.
    #[inline]
    pub fn from_reader<R>(data: R) -> ResponseBody where R: Read + Send + 'static {
        ResponseBody {
            data: Box::new(data),
            data_length: None,
        }
    }

    /// Builds a new `ResponseBody` that returns the given data.
    #[inline]
    pub fn from_data<D>(data: D) -> ResponseBody where D: Into<Vec<u8>> {
        let data = data.into();
        let len = data.len();

        ResponseBody {
            data: Box::new(Cursor::new(data)),
            data_length: Some(len),
        }
    }

    /// Builds a new `ResponseBody` that returns the content of the given file.
    #[inline]
    pub fn from_file(file: File) -> ResponseBody {
        let len = file.metadata().map(|metadata| metadata.len() as usize).ok();

        ResponseBody {
            data: Box::new(file),
            data_length: len,
        }
    }

    /// Builds a new `ResponseBody` that returns an UTF-8 string.
    #[inline]
    pub fn from_string<S>(data: S) -> ResponseBody where S: Into<String> {
        ResponseBody::from_data(data.into().into_bytes())
    }
}
<commit_msg>Minor hack for port 8001<commit_after>extern crate rand;
extern crate rustc_serialize;
extern crate time;
extern crate tiny_http;
extern crate url;

pub use assets::match_assets;
pub use log::LogEntry;
pub use input::{SessionsManager, Session, generate_session_id};

use std::io;
use std::io::Cursor;
use std::io::Read;
use std::error;
use std::fmt;
use std::fs::File;
use std::net::SocketAddr;
use std::net::ToSocketAddrs;
use std::str::FromStr;
use std::sync::{Arc, Mutex};
use std::thread;

pub mod input;

mod assets;
mod find_route;
mod log;
mod router;

/// An error that one of your routes can return.
///
/// This is just a convenience enum and you don't need to use it in your project
/// if you don't want to.
#[derive(Clone, Debug, PartialEq, Eq)]
pub enum RouteError {
    /// Couldn't find a way to handle this request.
    NoRouteFound,

    /// The user input is wrong.
    WrongInput,

    /// The user must be logged in.
    LoginRequired,

    /// The user entered a wrong login or password.
    WrongLoginPassword,

    /// The user is logged in but shouldn't be there.
    NotAuthorized,
}

impl error::Error for RouteError {
    fn description(&self) -> &str {
        match self {
            &RouteError::NoRouteFound => {
                "Couldn't find a way to handle this request."
            },
            &RouteError::WrongInput => {
                "The body of the request is malformed or missing something."
            },
            &RouteError::LoginRequired => {
                "The client must be logged in before this request can be answered."
            },
            &RouteError::WrongLoginPassword => {
                "The client attempted to login but entered a wrong login or password."
            },
            &RouteError::NotAuthorized => {
                "The client is logged in but doesn't have the permission to access this resource."
            },
        }
    }
}

impl fmt::Display for RouteError {
    fn fmt(&self, fmt: &mut fmt::Formatter) -> Result<(), fmt::Error> {
        write!(fmt, "{}", error::Error::description(self))
    }
}

/// Starts a server and uses the given requests handler.
///
/// The request handler takes a `&Request` and must return a `Response` to send to the user.
///
/// # Common mistakes
///
/// The handler must capture its environment by value and not by reference (`'static`). If you
/// use closure, don't forget to put `move` in front of the closure.
///
/// The handler must also be thread-safe (`Send` and `Sync`).
/// For example this handler isn't thread-safe:
///
/// ```ignore
/// let mut requests_counter = 0;
///
/// rouille::start_server("localhost:80", move |request| {
///     requests_counter += 1;
///
///     // rest of the handler
/// })
/// ```
///
/// Multiple requests can be processed simultaneously, therefore you can't mutably access
/// variables from the outside.
///
/// Instead you must use a `Mutex`:
///
/// ```no_run
/// use std::sync::Mutex;
/// let requests_counter = Mutex::new(0);
///
/// rouille::start_server("localhost:80", move |request| {
///     *requests_counter.lock().unwrap() += 1;
///
///     // rest of the handler
/// # panic!()
/// })
/// ```
///
/// # Panic handling
///
/// If your request handler panicks, a 500 error will automatically be sent to the client.
///
pub fn start_server<A, F>(addr: A, handler: F) -> !
                          where A: ToSocketAddrs,
                                F: Send + Sync + 'static + Fn(&Request) -> Response
{
    let server = tiny_http::ServerBuilder::new().with_port(8001).build().unwrap();

    for mut request in server.incoming_requests() {
        // TODO: don't read the body in memory immediately
        let mut data = Vec::with_capacity(request.body_length().unwrap_or(0));
        request.as_reader().read_to_end(&mut data);     // TODO: handle error

        // building the `Request` object
        let rouille_request = Request {
            url: request.url().to_owned(),
            method: request.method().as_str().to_owned(),
            headers: request.headers().iter().map(|h| (h.field.to_string(), h.value.clone().into())).collect(),
            https: false,
            data: data,
            remote_addr: request.remote_addr().clone(),
        };

        // calling the handler ; this most likely takes a lot of time
        let mut rouille_response = handler(&rouille_request);

        // writing the response
        let mut response = tiny_http::Response::empty(rouille_response.status_code)
                    .with_data(rouille_response.data.data, rouille_response.data.data_length);

        for (key, value) in rouille_response.headers {
            if let Ok(header) = tiny_http::Header::from_bytes(key, value) {
                response.add_header(header);
            } else {
                // TODO: ?
            }
        }

        request.respond(response);
    }

    unreachable!()
}

/// Represents a request that your handler must answer to.
///
/// This can be either a real request (received by the HTTP server) or a mock object created with
/// one of the `fake_*` constructors.
pub struct Request {
    method: String,
    url: String,
    headers: Vec<(String, String)>,
    https: bool,
    data: Vec<u8>,
    remote_addr: SocketAddr,
}

impl Request {
    /// Builds a fake HTTP request to be used during tests.
    ///
    /// The remote address of the client will be `127.0.0.1:12345`. Use `fake_http_from` to
    /// specify what the client's address should be.
    pub fn fake_http<U, M>(method: M, url: U, headers: Vec<(String, String)>, data: Vec<u8>)
                           -> Request where U: Into<String>, M: Into<String>
    {
        Request {
            url: url.into(),
            method: method.into(),
            https: false,
            data: data,
            headers: headers,
            remote_addr: "127.0.0.1:12345".parse().unwrap(),
        }
    }

    /// Builds a fake HTTP request to be used during tests.
    pub fn fake_http_from<U, M>(from: SocketAddr, method: M, url: U,
                                headers: Vec<(String, String)>, data: Vec<u8>)
                                -> Request where U: Into<String>, M: Into<String>
    {
        Request {
            url: url.into(),
            method: method.into(),
            https: false,
            data: data,
            headers: headers,
            remote_addr: from,
        }
    }

    /// Builds a fake HTTPS request to be used during tests.
    ///
    /// The remote address of the client will be `127.0.0.1:12345`. Use `fake_https_from` to
    /// specify what the client's address should be.
    pub fn fake_https<U, M>(method: M, url: U, headers: Vec<(String, String)>, data: Vec<u8>)
                            -> Request where U: Into<String>, M: Into<String>
    {
        Request {
            url: url.into(),
            method: method.into(),
            https: true,
            data: data,
            headers: headers,
            remote_addr: "127.0.0.1:12345".parse().unwrap(),
        }
    }

    /// Builds a fake HTTPS request to be used during tests.
    pub fn fake_https_from<U, M>(from: SocketAddr, method: M, url: U,
                                 headers: Vec<(String, String)>, data: Vec<u8>)
                                 -> Request where U: Into<String>, M: Into<String>
    {
        Request {
            url: url.into(),
            method: method.into(),
            https: true,
            data: data,
            headers: headers,
            remote_addr: from,
        }
    }

    /// Returns `true` if the request uses HTTPS instead of HTTP.
    #[inline]
    pub fn secure(&self) -> bool {
        self.https
    }

    /// Returns the method of the request (`GET`, `POST`, etc.).
    #[inline]
    pub fn method(&self) -> &str {
        &self.method
    }

    /// Returns the URL requested by the client. It is not decoded and thus can contain strings
    /// such as `%20`.
    ///
    /// See also `url()`.
    #[inline]
    pub fn raw_url(&self) -> &str {
        &self.url
    }

    /// Returns the URL requested by the client.
    ///
    /// Contrary to `raw_url`, special characters have been decoded.
    /// If there is any non-unicode character in the URL, it will be replaced with `U+FFFD`.
    pub fn url(&self) -> String {
        url::percent_encoding::lossy_utf8_percent_decode(self.url.as_bytes())
    }

    /// Returns the value of a header of the request.
    ///
    /// Returns `None` if no such header could be found.
    #[inline]
    pub fn header(&self, key: &str) -> Option<String> {
        self.headers.iter().find(|&&(ref k, _)| k == key).map(|&(_, ref v)| v.clone())
    }

    /// UNSTABLE. Returns the body of the request.
    ///
    /// Will eventually return an object that implements `Read` instead of a `Vec<u8>`.
    pub fn data(&self) -> Vec<u8> {
        self.data.clone()
    }

    /// Returns the address of the client that made this request.
    #[inline]
    pub fn remote_addr(&self) -> &SocketAddr {
        &self.remote_addr
    }
}

/// Contains a prototype of a response.
/// The response is only sent when you call `Request::respond`.
pub struct Response {
    /// The status code to return to the user.
    pub status_code: u16,

    /// List of headers to be returned in the response.
    ///
    /// Note that important headers such as `Connection` or `Content-Length` will be ignored
    /// from this list.
    // TODO: document precisely which headers
    pub headers: Vec<(String, String)>,

    /// An opaque type that contains the body of the response.
    pub data: ResponseBody,
}

impl Response {
    /// UNSTABLE. Builds a default response to handle the given route error.
    ///
    /// Important: don't use this in a real website. This function is just a convenience when
    /// prototyping.
    ///
    /// For authentication-related errors, you are strongly encouraged to handle them yourself.
    #[inline]
    pub fn from_error(err: &RouteError) -> Response {
        match err {
            &RouteError::NoRouteFound => Response {
                status_code: 404, headers: vec![], data: ResponseBody::empty()
            },
            &RouteError::WrongInput => Response {
                status_code: 400, headers: vec![], data: ResponseBody::empty()
            },
            &RouteError::LoginRequired => Response {
                status_code: 401, headers: vec![], data: ResponseBody::empty()
            },     // TODO: www-auth header?
            &RouteError::WrongLoginPassword => Response {
                status_code: 401, headers: vec![], data: ResponseBody::empty()
            },     // TODO: www-auth header?
            &RouteError::NotAuthorized => Response {
                status_code: 403, headers: vec![], data: ResponseBody::empty()
            },
        }
    }

    /// Builds a `Response` that redirects the user to another URL.
    #[inline]
    pub fn redirect(target: &str) -> Response {
        Response {
            status_code: 303,
            headers: vec![("Location".to_owned(), target.to_owned())],
            data: ResponseBody::empty(),
        }
    }

    /// Builds a `Response` that outputs HTML.
    #[inline]
    pub fn html<D>(content: D) -> Response where D: Into<Vec<u8>> {
        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "text/html; charset=utf8".to_owned())],
            data: ResponseBody::from_data(content),
        }
    }

    /// Builds a `Response` that outputs plain text.
    #[inline]
    pub fn text<S>(text: S) -> Response where S: Into<String> {
        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "text/plain; charset=utf8".to_owned())],
            data: ResponseBody::from_string(text),
        }
    }

    /// Builds a `Response` that outputs JSON.
    #[inline]
    pub fn json<T>(content: &T) -> Response where T: rustc_serialize::Encodable {
        let data = rustc_serialize::json::encode(content).unwrap();

        Response {
            status_code: 200,
            headers: vec![("Content-Type".to_owned(), "application/json".to_owned())],
            data: ResponseBody::from_data(data),
        }
    }

    /// Builds a `Response` that returns a `401 Not Authorized` status
    /// and a `WWW-Authenticate` header.
    #[inline]
    pub fn basic_http_auth_login_required(realm: &str) -> Response {
        // TODO: escape the realm
        Response {
            status_code: 401,
            headers: vec![("WWW-Authenticate".to_owned(), format!("Basic realm=\"{}\"", realm))],
            data: ResponseBody::empty(),
        }
    }

    /// Changes the status code of the response.
    #[inline]
    pub fn with_status_code(mut self, code: u16) -> Response {
        self.status_code = code;
        self
    }
}

/// An opaque type that represents the body of a response.
pub struct ResponseBody {
    data: Box<Read + Send>,
    data_length: Option<usize>,
}

impl ResponseBody {
    /// Builds a `ResponseBody` that doesn't return any data.
    #[inline]
    pub fn empty() -> ResponseBody {
        ResponseBody {
            data: Box::new(io::empty()),
            data_length: Some(0),
        }
    }

    /// Builds a new `ResponseBody` that will read the data from a `Read`.
    ///
    /// Note that this is suboptimal compared to other constructors because the length
    /// isn't known in advance.
    #[inline]
    pub fn from_reader<R>(data: R) -> ResponseBody where R: Read + Send + 'static {
        ResponseBody {
            data: Box::new(data),
            data_length: None,
        }
    }

    /// Builds a new `ResponseBody` that returns the given data.
    #[inline]
    pub fn from_data<D>(data: D) -> ResponseBody where D: Into<Vec<u8>> {
        let data = data.into();
        let len = data.len();

        ResponseBody {
            data: Box::new(Cursor::new(data)),
            data_length: Some(len),
        }
    }

    /// Builds a new `ResponseBody` that returns the content of the given file.
    #[inline]
    pub fn from_file(file: File) -> ResponseBody {
        let len = file.metadata().map(|metadata| metadata.len() as usize).ok();

        ResponseBody {
            data: Box::new(file),
            data_length: len,
        }
    }

    /// Builds a new `ResponseBody` that returns an UTF-8 string.
    #[inline]
    pub fn from_string<S>(data: S) -> ResponseBody where S: Into<String> {
        ResponseBody::from_data(data.into().into_bytes())
    }
}
<|endoftext|>


<commit_before>//! A data type suitable for storing sensitive information such as passwords and private keys in memory, featuring constant time equality, mlock and zeroing out.
#[cfg(feature = "cbor-serialize")] extern crate cbor;
#[cfg(feature = "cbor-serialize")] extern crate rustc_serialize;
use std::fmt;
use std::borrow::{Borrow, BorrowMut};
#[cfg(feature = "cbor-serialize")] use rustc_serialize::{Decoder, Encoder, Decodable, Encodable};

/**
 * Obtain the number of bytes stored in the given byte slice
 */
fn size_of<T: Sized>(slice: &[T]) -> usize {
    slice.len() * std::mem::size_of::<T>()
}

#[cfg(feature = "libsodium-sys")]
mod mem {
    extern crate libsodium_sys as sodium;
    use ::size_of;

    pub fn zero<T: Sized + Copy>(slice: &mut [T]) {
        unsafe {
            sodium::sodium_memzero(slice.as_ptr() as *mut u8, size_of(slice));
        }
    }

    pub fn cmp<T: Sized + Copy>(us: &[T], them: &[T]) -> bool {
        if us.len() != them.len() {
            return false;
        }

        unsafe {
            sodium::sodium_memcmp(us.as_ptr() as *const u8, them.as_ptr() as *const u8, size_of(them)) == 0
        }
    }
}

#[cfg(not(feature = "libsodium-sys"))]
mod mem {
    use std;

    use ::size_of;

    #[inline(never)]
    pub fn zero<T: Sized + Copy>(slice: &mut [T]) {
        let ptr = slice.as_mut_ptr() as *mut u8;
        for i in 0 .. size_of(slice) {
            unsafe {
                std::ptr::write_volatile(ptr.offset(i as isize), 0);
            }
        }
    }

    #[inline(never)]
    pub fn cmp<T: Sized + Copy>(us: &[T], them: &[T]) -> bool {
        if us.len() != them.len() {
            return false;
        }

        let mut result: u8 = 0;

        let ptr_us   = us.as_ptr()   as *mut u8;
        let ptr_them = them.as_ptr() as *mut u8;
        for i in 0 .. size_of(us) {
            unsafe {
                result |= *(ptr_us.offset(i as isize)) ^ *(ptr_them.offset(i as isize));
            }
        }

        result == 0
    }
}

#[cfg(unix)]
mod memlock {
    extern crate libc;

    use ::size_of;

    pub fn mlock<T: Sized>(cont: &[T]) {
        unsafe {
            libc::mlock(cont.as_ptr() as *const libc::c_void, size_of(cont));
        }
    }

    pub fn munlock<T: Sized>(cont: &[T]) {
        unsafe {
            libc::munlock(cont.as_ptr() as *const libc::c_void, size_of(cont));
        }
    }
}

#[cfg(not(unix))]
mod memlock {
    fn mlock<T: Sized>(cont: &[T]) {
    }

    fn munlock<T: Sized>(cont: &[T]) {
    }
}

/// Type alias for a vector that stores just bytes
pub type SecStr = SecVec<u8>;

/// A data type suitable for storing sensitive information such as passwords and private keys in memory, that implements:
///
/// - Automatic zeroing in `Drop`
/// - Constant time comparison in `PartialEq` (does not short circuit on the first different character; but terminates instantly if strings have different length)
/// - Outputting `***SECRET***` to prevent leaking secrets into logs in `fmt::Debug` and `fmt::Display`
/// - Automatic `mlock` to protect against leaking into swap
///
/// Be careful with `SecStr::from`: if you have a borrowed string, it will be copied.
/// Use `SecStr::new` if you have a `Vec<u8>`.
#[derive(Clone, Eq)]
pub struct SecVec<T> where T: Sized + Copy {
    content: Vec<T>
}

impl<T> SecVec<T> where T: Sized + Copy {
    pub fn new(cont: Vec<T>) -> Self {
        memlock::mlock(&cont);
        SecVec { content: cont }
    }

    /// Borrow the contents of the string.
    pub fn unsecure(&self) -> &[T] {
        self.borrow()
    }

    /// Mutably borrow the contents of the string.
    pub fn unsecure_mut(&mut self) -> &mut [T] {
        self.borrow_mut()
    }

    /// Overwrite the string with zeros. This is automatically called in the destructor.
    pub fn zero_out(&mut self) {
        mem::zero(&mut self.content);
    }
}

// Creation
impl<T, U> From<U> for SecVec<T> where U: Into<Vec<T>>, T: Sized + Copy {
    fn from(s: U) -> SecVec<T> {
        SecVec::new(s.into())
    }
}

// Borrowing
impl<T> Borrow<[T]> for SecVec<T> where T: Sized + Copy {
    fn borrow(&self) -> &[T] {
        self.content.borrow()
    }
}

impl<T> BorrowMut<[T]> for SecVec<T> where T: Sized + Copy {
    fn borrow_mut(&mut self) -> &mut [T] {
        self.content.borrow_mut()
    }
}

// Overwrite memory with zeros when we're done
impl<T> Drop for SecVec<T> where T: Sized + Copy {
    fn drop(&mut self) {
        self.zero_out();
        memlock::munlock(&self.content);
    }
}

// Constant time comparison
impl<T> PartialEq for SecVec<T> where T: Sized + Copy {
    fn eq(&self, other: &SecVec<T>) -> bool {
        mem::cmp(&self.content, &other.content)
    }
}

// Make sure sensitive information is not logged accidentally
impl<T> fmt::Debug for SecVec<T> where T: Sized + Copy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("***SECRET***").map_err(|_| { fmt::Error })
    }
}

impl<T> fmt::Display for SecVec<T> where T: Sized + Copy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("***SECRET***").map_err(|_| { fmt::Error })
    }
}

#[cfg(feature = "cbor-serialize")]
impl<T> Decodable for SecVec<T> where T: Sized + Copy {
    fn decode<D: Decoder>(d: &mut D) -> Result<SecStr, D::Error> {
        let cbor::CborBytes(content) = try!(cbor::CborBytes::decode(d));
        Ok(SecVec::<T>::new(content))
    }
}

#[cfg(feature = "cbor-serialize")]
impl<T> Encodable for SecVec<T> where T: Sized + Copy {
    fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
        cbor::CborBytes(self.content.clone()).encode(e)
    }
}

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

    #[test]
    fn test_basic() {
        let my_sec = SecStr::from("hello");
        assert_eq!(my_sec, SecStr::from("hello".to_string()));
        assert_eq!(my_sec.unsecure(), b"hello");
    }

    #[test]
    fn test_zero_out() {
        let mut my_sec = SecStr::from("hello");
        my_sec.zero_out();
        assert_eq!(my_sec.unsecure(), b"\x00\x00\x00\x00\x00");
    }

    #[test]
    fn test_comparison() {
        assert_eq!(SecStr::from("hello"),  SecStr::from("hello"));
        assert!(  SecStr::from("hello") != SecStr::from("yolo"));
        assert!(  SecStr::from("hello") != SecStr::from("olleh"));
        assert!(  SecStr::from("hello") != SecStr::from("helloworld"));
        assert!(  SecStr::from("hello") != SecStr::from(""));
    }

    #[test]
    fn test_show() {
        assert_eq!(format!("{}", SecStr::from("hello")), "***SECRET***".to_string());
    }

}
<commit_msg>Add unit test for `StrVec<char>`<commit_after>//! A data type suitable for storing sensitive information such as passwords and private keys in memory, featuring constant time equality, mlock and zeroing out.
#[cfg(feature = "cbor-serialize")] extern crate cbor;
#[cfg(feature = "cbor-serialize")] extern crate rustc_serialize;
use std::fmt;
use std::borrow::{Borrow, BorrowMut};
#[cfg(feature = "cbor-serialize")] use rustc_serialize::{Decoder, Encoder, Decodable, Encodable};

/**
 * Obtain the number of bytes stored in the given byte slice
 */
fn size_of<T: Sized>(slice: &[T]) -> usize {
    slice.len() * std::mem::size_of::<T>()
}

#[cfg(feature = "libsodium-sys")]
mod mem {
    extern crate libsodium_sys as sodium;
    use ::size_of;

    pub fn zero<T: Sized + Copy>(slice: &mut [T]) {
        unsafe {
            sodium::sodium_memzero(slice.as_ptr() as *mut u8, size_of(slice));
        }
    }

    pub fn cmp<T: Sized + Copy>(us: &[T], them: &[T]) -> bool {
        if us.len() != them.len() {
            return false;
        }

        unsafe {
            sodium::sodium_memcmp(us.as_ptr() as *const u8, them.as_ptr() as *const u8, size_of(them)) == 0
        }
    }
}

#[cfg(not(feature = "libsodium-sys"))]
mod mem {
    use std;

    use ::size_of;

    #[inline(never)]
    pub fn zero<T: Sized + Copy>(slice: &mut [T]) {
        let ptr = slice.as_mut_ptr() as *mut u8;
        for i in 0 .. size_of(slice) {
            unsafe {
                std::ptr::write_volatile(ptr.offset(i as isize), 0);
            }
        }
    }

    #[inline(never)]
    pub fn cmp<T: Sized + Copy>(us: &[T], them: &[T]) -> bool {
        if us.len() != them.len() {
            return false;
        }

        let mut result: u8 = 0;

        let ptr_us   = us.as_ptr()   as *mut u8;
        let ptr_them = them.as_ptr() as *mut u8;
        for i in 0 .. size_of(us) {
            unsafe {
                result |= *(ptr_us.offset(i as isize)) ^ *(ptr_them.offset(i as isize));
            }
        }

        result == 0
    }
}

#[cfg(unix)]
mod memlock {
    extern crate libc;

    use ::size_of;

    pub fn mlock<T: Sized>(cont: &[T]) {
        unsafe {
            libc::mlock(cont.as_ptr() as *const libc::c_void, size_of(cont));
        }
    }

    pub fn munlock<T: Sized>(cont: &[T]) {
        unsafe {
            libc::munlock(cont.as_ptr() as *const libc::c_void, size_of(cont));
        }
    }
}

#[cfg(not(unix))]
mod memlock {
    fn mlock<T: Sized>(cont: &[T]) {
    }

    fn munlock<T: Sized>(cont: &[T]) {
    }
}

/// Type alias for a vector that stores just bytes
pub type SecStr = SecVec<u8>;

/// A data type suitable for storing sensitive information such as passwords and private keys in memory, that implements:
///
/// - Automatic zeroing in `Drop`
/// - Constant time comparison in `PartialEq` (does not short circuit on the first different character; but terminates instantly if strings have different length)
/// - Outputting `***SECRET***` to prevent leaking secrets into logs in `fmt::Debug` and `fmt::Display`
/// - Automatic `mlock` to protect against leaking into swap
///
/// Be careful with `SecStr::from`: if you have a borrowed string, it will be copied.
/// Use `SecStr::new` if you have a `Vec<u8>`.
#[derive(Clone, Eq)]
pub struct SecVec<T> where T: Sized + Copy {
    content: Vec<T>
}

impl<T> SecVec<T> where T: Sized + Copy {
    pub fn new(cont: Vec<T>) -> Self {
        memlock::mlock(&cont);
        SecVec { content: cont }
    }

    /// Borrow the contents of the string.
    pub fn unsecure(&self) -> &[T] {
        self.borrow()
    }

    /// Mutably borrow the contents of the string.
    pub fn unsecure_mut(&mut self) -> &mut [T] {
        self.borrow_mut()
    }

    /// Overwrite the string with zeros. This is automatically called in the destructor.
    pub fn zero_out(&mut self) {
        mem::zero(&mut self.content);
    }
}

// Creation
impl<T, U> From<U> for SecVec<T> where U: Into<Vec<T>>, T: Sized + Copy {
    fn from(s: U) -> SecVec<T> {
        SecVec::new(s.into())
    }
}

// Borrowing
impl<T> Borrow<[T]> for SecVec<T> where T: Sized + Copy {
    fn borrow(&self) -> &[T] {
        self.content.borrow()
    }
}

impl<T> BorrowMut<[T]> for SecVec<T> where T: Sized + Copy {
    fn borrow_mut(&mut self) -> &mut [T] {
        self.content.borrow_mut()
    }
}

// Overwrite memory with zeros when we're done
impl<T> Drop for SecVec<T> where T: Sized + Copy {
    fn drop(&mut self) {
        self.zero_out();
        memlock::munlock(&self.content);
    }
}

// Constant time comparison
impl<T> PartialEq for SecVec<T> where T: Sized + Copy {
    fn eq(&self, other: &SecVec<T>) -> bool {
        mem::cmp(&self.content, &other.content)
    }
}

// Make sure sensitive information is not logged accidentally
impl<T> fmt::Debug for SecVec<T> where T: Sized + Copy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("***SECRET***").map_err(|_| { fmt::Error })
    }
}

impl<T> fmt::Display for SecVec<T> where T: Sized + Copy {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        f.write_str("***SECRET***").map_err(|_| { fmt::Error })
    }
}

#[cfg(feature = "cbor-serialize")]
impl<T> Decodable for SecVec<T> where T: Sized + Copy {
    fn decode<D: Decoder>(d: &mut D) -> Result<SecStr, D::Error> {
        let cbor::CborBytes(content) = try!(cbor::CborBytes::decode(d));
        Ok(SecVec::<T>::new(content))
    }
}

#[cfg(feature = "cbor-serialize")]
impl<T> Encodable for SecVec<T> where T: Sized + Copy {
    fn encode<E: Encoder>(&self, e: &mut E) -> Result<(), E::Error> {
        cbor::CborBytes(self.content.clone()).encode(e)
    }
}

#[cfg(test)]
mod tests {
    use super::{SecStr, SecVec};

    #[test]
    fn test_basic() {
        let my_sec = SecStr::from("hello");
        assert_eq!(my_sec, SecStr::from("hello".to_string()));
        assert_eq!(my_sec.unsecure(), b"hello");
    }

    #[test]
    fn test_zero_out() {
        let mut my_sec = SecStr::from("hello");
        my_sec.zero_out();
        assert_eq!(my_sec.unsecure(), b"\x00\x00\x00\x00\x00");
    }

    #[test]
    fn test_comparison() {
        assert_eq!(SecStr::from("hello"),  SecStr::from("hello"));
        assert!(  SecStr::from("hello") != SecStr::from("yolo"));
        assert!(  SecStr::from("hello") != SecStr::from("olleh"));
        assert!(  SecStr::from("hello") != SecStr::from("helloworld"));
        assert!(  SecStr::from("hello") != SecStr::from(""));
    }

    #[test]
    fn test_show() {
        assert_eq!(format!("{}", SecStr::from("hello")), "***SECRET***".to_string());
    }

    #[test]
    fn test_comparison_zero_out_mb() {
        let mbstring1 = SecVec::from(vec!['H','a','l','l','o',' ','🦄','!']);
        let mbstring2 = SecVec::from(vec!['H','a','l','l','o',' ','🦄','!']);
        let mbstring3 = SecVec::from(vec!['!','🦄',' ','o','l','l','a','H']);
        assert!(mbstring1 == mbstring2);
        assert!(mbstring1 != mbstring3);

        let mut mbstring = mbstring1.clone();
        mbstring.zero_out();
        assert_eq!(mbstring.unsecure(), &['\0','\0','\0','\0','\0','\0','\0','\0']);
    }
}
<|endoftext|>