<commit_before>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/Luz/hexdino/master/logo.png")]

use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::SeekFrom;
use std::path::Path;
use std::error::Error;
use std::env;

mod draw;
mod find;
use draw::draw;
use find::FindSubset;

extern crate ncurses;
use ncurses::*;

// extern crate getopts;
// use getopts::Options;

#[derive(PartialEq, Copy, Clone)]
pub enum Mode {
    Command,
    Replace,
    TypeCommand,
    Insert,
    TypeSearch,
    SearchIt,
    SearchItHex,
}

fn main() {
    let mut buf = vec![];
    let mut cursorpos: usize = 0;
    // 0 is left nibble, 1 is right nibble, 2 is ascii
    let mut cursorstate: usize = 0;
    // 0 = display data from first line of file
    let mut screenoffset: usize = 0;
    const SPALTEN: usize = 16;
    let mut command = String::new();

    // start ncursesw
    initscr();
    let screenheight: usize = getmaxy(stdscr) as usize;
    // ctrl+z and fg works with this
    cbreak();
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    printw("Welcome to Hexdino.\nPlease find the any-key on your keyboard and press it.\n");

    let args: Vec<_> = env::args().collect();
    // let program = args[0].clone();
    // let mut opts = Options::new();
    // opts.optopt("f", "", "set file name", "NAME");
    // opts.optflag("h", "help", "print this help menu");
    //
    // let matches = match opts.parse(&args[1..]) {
    // Ok(m) => { m }
    // Err(f) => { panic!(f.to_string()) }
    // };
    // if matches.opt_present("h") {
    // let brief = format!("Usage: {} FILE [options]", program);
    // print!("{}", opts.usage(&brief));
    // return;
    // }
    // let path = if !matches.free.is_empty() {
    // matches.free[0].clone()
    // } else {
    // let brief = format!("Usage: {} FILE [options]", program);
    // print!("{}", opts.usage(&brief));
    // return;
    // };

    let path = if args.len() > 1 {
        printw(&format!("Opening file {}", args[1]));
        args[1].clone()
    } else {
        printw(&format!("No file specified. Trying to open foo.txt"));
        "foo.txt".into()
    };

    let path = Path::new(&path);
    let display = path.display();

    getch();
    clear();

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

    // TODO in this order: 1)disp files 2) hjkl movement 3) edit file by 'r' 4) save file 5) edit file by 'x, i' 6) search by '/'

    let mut file = match OpenOptions::new().read(true).write(true).create(true).open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    file.read_to_end(&mut buf).ok().expect("File could not be read.");

    let mut mode = Mode::Command;

    draw(&buf,
         cursorpos,
         SPALTEN,
         mode,
         &command,
         cursorstate,
         screenoffset);

    let mut key;
    //    key = getch() as u8;
    //    printw(&format!("{:?}", key));

    let mut quitnow = 0;
    while quitnow == 0 {
        key = getch() as u8;
        if mode == Mode::Command {
            match key as char {
                'h' => {
                    if cursorstate == 2 {
                        // ascii mode
                        if cursorpos > 0 {
                            // not at start
                            cursorpos -= 1; // go left
                        }
                    } else if cursorstate == 1 {
                        // hex mode, right nibble
                        cursorstate = 0; // left nibble
                    } else if cursorstate == 0 {
                        // hex mode, left nibble
                        if cursorpos > 0 {
                            // not at start
                            cursorstate = 1; // right nibble
                            cursorpos -= 1; // go left
                        }
                    }
                }
                'j' => {
                    if cursorpos + SPALTEN < buf.len() {
                        // not at end
                        cursorpos += SPALTEN; // go down
                    } else {
                        // when at end
                        if buf.len() != 0 {
                            // Suppress underflow
                            cursorpos = buf.len() - 1; // go to end
                        }
                    }
                }
                'k' => {
                    if cursorpos >= SPALTEN {
                        cursorpos -= SPALTEN;
                    }
                }
                'l' => {
                    if cursorstate == 2 {
                        // ascii mode
                        if cursorpos + 1 < buf.len() {
                            // not at end
                            cursorpos += 1; // go right
                        }
                    } else if cursorstate == 0 {
                        // hex mode, left nibble
                        cursorstate = 1; // right nibble
                    } else if cursorstate == 1 {
                        // hex mode, right nibble
                        if cursorpos + 1 < buf.len() {
                            // not at end
                            cursorstate = 0; // left nibble
                            cursorpos += 1; // go right
                        }
                    }
                }
                '0' => {
                    cursorpos -= cursorpos % SPALTEN; // jump to start of line
                    if cursorstate == 1 {
                        // hex mode, right nibble
                        cursorstate = 0; // left nibble
                    }
                }
                '$' => {
                    if cursorpos - (cursorpos % SPALTEN) + (SPALTEN - 1) < buf.len() {
                        // check if no overflow
                        cursorpos = cursorpos - (cursorpos % SPALTEN) + (SPALTEN - 1); // jump to end of line
                    } else {
                        cursorpos = buf.len() - 1 // jump to end of line
                    }
                    if cursorstate == 0 {
                        // hex mode, left nibble
                        cursorstate = 1; // right nibble
                    }
                }
                'r' => {
                    mode = Mode::Replace;
                }
                'x' => {
                    if buf.len() > 0 {
                        // remove the current char
                        buf.remove(cursorpos);
                        if cursorpos >= buf.len() && cursorpos > 0 {
                            cursorpos -= 1;
                        }
                    }
                }
                'i' => {
                    mode = Mode::Insert;
                }
                ':' => {
                    command.clear(); // delete old command
                    mode = Mode::TypeCommand;
                }
                'J' => {
                    if cursorstate == 2 {
                        // ascii mode
                        cursorstate = 0; // hex mode, left nibble
                    } else {
                        cursorstate = 2; // jump to ascii
                    }
                }
                '?' => {
                    // TODO
                    command.push_str("No helpfile yet");
                }
                '/' => {
                    // TODO
                    command.clear();
                    mode = Mode::TypeSearch;
                }
                _ => (),
            }

            // Always move screen when cursor leaves screen
            if cursorpos > (screenheight + screenoffset - 1) * SPALTEN - 1 {
                screenoffset += 1; // move screen
            }
            if cursorpos < screenoffset * SPALTEN {
                screenoffset -= 1; // move screen
            }

        } else if mode == Mode::Replace && cursorstate == 2 {
            // r was pressed so replace next char in ascii mode
            if cursorpos >= buf.len() {
                buf.insert(cursorpos, 0 );
            }
            match key {
                c @ 32...126 => buf[cursorpos] = c,
                _ => (),
            }
            mode = Mode::Command;
        } else if mode == Mode::Replace {
            let mask = if cursorstate == 0 { 0x0F } else { 0xF0 };
            let shift = if cursorstate == 0 { 4 } else { 0 };
            if cursorpos >= buf.len() {
                buf.insert(cursorpos, 0 );
            }
            // Change the selected nibble
            if let Some(c) = (key as char).to_digit(16) {
                buf[cursorpos] = buf[cursorpos] & mask | (c as u8) << shift;
            }
            mode = Mode::Command;
        } else if mode == Mode::TypeCommand {
            match key {
                c @ 32...126 => {
                    command.push(c as char);
                }
                27 => {
                    command.clear();
                    mode = Mode::Command;
                }
                10 => {
                    // Enter pressed, compute command!
                    if (command == "w".to_string()) || (command == "wq".to_string()) {
                        file.seek(SeekFrom::Start(0))
                            .ok()
                            .expect("Filepointer could not be set to 0");
                        file.write_all(&mut buf).ok().expect("File could not be written.");
                        if command == "wq".to_string() {
                            quitnow = 1;
                        }
                        command.clear();
                        mode = Mode::Command;
                    } else if command == "q".to_string() {
                        quitnow = 1;
                    } else {
                        command.clear();
                        command.push_str("Bad_command!");
                        mode = Mode::Command;
                    }
                }
                _ => (),
            }
        } else if mode == Mode::Insert {
            if cursorstate == 0 {
                // Left nibble
                if let Some(c) = (key as char).to_digit(16) {
                    buf.insert(cursorpos, (c as u8) << 4); cursorstate = 1;
                }
                if key == 27 {mode = Mode::Command;};
            } else if cursorstate == 1 {
                // Right nibble
                if cursorpos == buf.len() {
                    buf.insert(cursorpos, 0 );
                }
                if let Some(c) = (key as char).to_digit(16) {
                    buf[cursorpos] = buf[cursorpos]&0xF0 | c as u8; cursorstate = 0; cursorpos+=1;
                }
                if key == 27 {mode = Mode::Command;};
            } else if cursorstate == 2 {
                // Ascii
                match key {
                    c @ 32...126 => {
                        buf.insert(cursorpos, c);
                        cursorpos += 1;
                    }
                    27 => {
                        mode = Mode::Command;
                    }
                    _ => (),
                }
            }
        } else if mode == Mode::TypeSearch {
            //            if cursorstate == 2 { // Ascii search
            match key {
                c @ 32...126 => {
                    command.push(c as char);
                }
                27 => {
                    command.clear();
                    mode = Mode::Command;
                }
                10 => {
                    // Enter pressed, compute command!
                    if cursorstate == 2 {
                        mode = Mode::SearchIt;
                    } else {
                        mode = Mode::SearchItHex;
                    }
                }
                _ => (),
            }
            //            }

            if mode == Mode::SearchIt {
                let search = command.as_bytes();
                cursorpos = buf.find_subset(&search).unwrap_or(cursorpos);
                mode = Mode::Command;
                // command.push_str("Bad_command!");
            }
            if mode == Mode::SearchItHex {
                // TODO
                mode = Mode::Command;
                command.push_str("Hex search not yet implemented!");
            }
        }
        draw(&buf,
             cursorpos,
             SPALTEN,
             mode,
             &command,
             cursorstate,
             screenoffset);
    }

    refresh();
    endwin();
}
<commit_msg>Close issue: Truncating to correct length on filewrite<commit_after>//! # Hexdino
//!
//! A hex editor with vim like keybindings written in Rust.

#![doc(html_logo_url = "https://raw.githubusercontent.com/Luz/hexdino/master/logo.png")]

use std::io::prelude::*;
use std::fs::OpenOptions;
use std::io::SeekFrom;
use std::path::Path;
use std::error::Error;
use std::env;

mod draw;
mod find;
use draw::draw;
use find::FindSubset;

extern crate ncurses;
use ncurses::*;

// extern crate getopts;
// use getopts::Options;

#[derive(PartialEq, Copy, Clone)]
pub enum Mode {
    Command,
    Replace,
    TypeCommand,
    Insert,
    TypeSearch,
    SearchIt,
    SearchItHex,
}

fn main() {
    let mut buf = vec![];
    let mut cursorpos: usize = 0;
    // 0 is left nibble, 1 is right nibble, 2 is ascii
    let mut cursorstate: usize = 0;
    // 0 = display data from first line of file
    let mut screenoffset: usize = 0;
    const SPALTEN: usize = 16;
    let mut command = String::new();

    // start ncursesw
    initscr();
    let screenheight: usize = getmaxy(stdscr) as usize;
    // ctrl+z and fg works with this
    cbreak();
    noecho();
    start_color();
    init_pair(1, COLOR_GREEN, COLOR_BLACK);

    printw("Welcome to Hexdino.\nPlease find the any-key on your keyboard and press it.\n");

    let args: Vec<_> = env::args().collect();
    // let program = args[0].clone();
    // let mut opts = Options::new();
    // opts.optopt("f", "", "set file name", "NAME");
    // opts.optflag("h", "help", "print this help menu");
    //
    // let matches = match opts.parse(&args[1..]) {
    // Ok(m) => { m }
    // Err(f) => { panic!(f.to_string()) }
    // };
    // if matches.opt_present("h") {
    // let brief = format!("Usage: {} FILE [options]", program);
    // print!("{}", opts.usage(&brief));
    // return;
    // }
    // let path = if !matches.free.is_empty() {
    // matches.free[0].clone()
    // } else {
    // let brief = format!("Usage: {} FILE [options]", program);
    // print!("{}", opts.usage(&brief));
    // return;
    // };

    let path = if args.len() > 1 {
        printw(&format!("Opening file {}", args[1]));
        args[1].clone()
    } else {
        printw(&format!("No file specified. Trying to open foo.txt"));
        "foo.txt".into()
    };

    let path = Path::new(&path);
    let display = path.display();

    getch();
    clear();

    if !has_colors() {
        endwin();
        println!("Your terminal does not support color!\n");
        return;
    }

    // TODO in this order: 1)disp files 2) hjkl movement 3) edit file by 'r' 4) save file 5) edit file by 'x, i' 6) search by '/'

    let mut file = match OpenOptions::new().read(true).write(true).create(true).open(&path) {
        Err(why) => panic!("couldn't open {}: {}", display, Error::description(&why)),
        Ok(file) => file,
    };

    file.read_to_end(&mut buf).ok().expect("File could not be read.");

    let mut mode = Mode::Command;

    draw(&buf,
         cursorpos,
         SPALTEN,
         mode,
         &command,
         cursorstate,
         screenoffset);

    let mut key;
    //    key = getch() as u8;
    //    printw(&format!("{:?}", key));

    let mut quitnow = 0;
    while quitnow == 0 {
        key = getch() as u8;
        if mode == Mode::Command {
            match key as char {
                'h' => {
                    if cursorstate == 2 {
                        // ascii mode
                        if cursorpos > 0 {
                            // not at start
                            cursorpos -= 1; // go left
                        }
                    } else if cursorstate == 1 {
                        // hex mode, right nibble
                        cursorstate = 0; // left nibble
                    } else if cursorstate == 0 {
                        // hex mode, left nibble
                        if cursorpos > 0 {
                            // not at start
                            cursorstate = 1; // right nibble
                            cursorpos -= 1; // go left
                        }
                    }
                }
                'j' => {
                    if cursorpos + SPALTEN < buf.len() {
                        // not at end
                        cursorpos += SPALTEN; // go down
                    } else {
                        // when at end
                        if buf.len() != 0 {
                            // Suppress underflow
                            cursorpos = buf.len() - 1; // go to end
                        }
                    }
                }
                'k' => {
                    if cursorpos >= SPALTEN {
                        cursorpos -= SPALTEN;
                    }
                }
                'l' => {
                    if cursorstate == 2 {
                        // ascii mode
                        if cursorpos + 1 < buf.len() {
                            // not at end
                            cursorpos += 1; // go right
                        }
                    } else if cursorstate == 0 {
                        // hex mode, left nibble
                        cursorstate = 1; // right nibble
                    } else if cursorstate == 1 {
                        // hex mode, right nibble
                        if cursorpos + 1 < buf.len() {
                            // not at end
                            cursorstate = 0; // left nibble
                            cursorpos += 1; // go right
                        }
                    }
                }
                '0' => {
                    cursorpos -= cursorpos % SPALTEN; // jump to start of line
                    if cursorstate == 1 {
                        // hex mode, right nibble
                        cursorstate = 0; // left nibble
                    }
                }
                '$' => {
                    if cursorpos - (cursorpos % SPALTEN) + (SPALTEN - 1) < buf.len() {
                        // check if no overflow
                        cursorpos = cursorpos - (cursorpos % SPALTEN) + (SPALTEN - 1); // jump to end of line
                    } else {
                        cursorpos = buf.len() - 1 // jump to end of line
                    }
                    if cursorstate == 0 {
                        // hex mode, left nibble
                        cursorstate = 1; // right nibble
                    }
                }
                'r' => {
                    mode = Mode::Replace;
                }
                'x' => {
                    if buf.len() > 0 {
                        // remove the current char
                        buf.remove(cursorpos);
                        if cursorpos >= buf.len() && cursorpos > 0 {
                            cursorpos -= 1;
                        }
                    }
                }
                'i' => {
                    mode = Mode::Insert;
                }
                ':' => {
                    command.clear(); // delete old command
                    mode = Mode::TypeCommand;
                }
                'J' => {
                    if cursorstate == 2 {
                        // ascii mode
                        cursorstate = 0; // hex mode, left nibble
                    } else {
                        cursorstate = 2; // jump to ascii
                    }
                }
                '?' => {
                    // TODO
                    command.push_str("No helpfile yet");
                }
                '/' => {
                    // TODO
                    command.clear();
                    mode = Mode::TypeSearch;
                }
                _ => (),
            }

            // Always move screen when cursor leaves screen
            if cursorpos > (screenheight + screenoffset - 1) * SPALTEN - 1 {
                screenoffset += 1; // move screen
            }
            if cursorpos < screenoffset * SPALTEN {
                screenoffset -= 1; // move screen
            }

        } else if mode == Mode::Replace && cursorstate == 2 {
            // r was pressed so replace next char in ascii mode
            if cursorpos >= buf.len() {
                buf.insert(cursorpos, 0 );
            }
            match key {
                c @ 32...126 => buf[cursorpos] = c,
                _ => (),
            }
            mode = Mode::Command;
        } else if mode == Mode::Replace {
            let mask = if cursorstate == 0 { 0x0F } else { 0xF0 };
            let shift = if cursorstate == 0 { 4 } else { 0 };
            if cursorpos >= buf.len() {
                buf.insert(cursorpos, 0 );
            }
            // Change the selected nibble
            if let Some(c) = (key as char).to_digit(16) {
                buf[cursorpos] = buf[cursorpos] & mask | (c as u8) << shift;
            }
            mode = Mode::Command;
        } else if mode == Mode::TypeCommand {
            match key {
                c @ 32...126 => {
                    command.push(c as char);
                }
                27 => {
                    command.clear();
                    mode = Mode::Command;
                }
                10 => {
                    // Enter pressed, compute command!
                    if (command == "w".to_string()) || (command == "wq".to_string()) {
                        file.seek(SeekFrom::Start(0))
                            .ok()
                            .expect("Filepointer could not be set to 0");
                        file.write_all(&mut buf).ok().expect("File could not be written.");
												file.set_len(buf.len() as u64).ok().expect("File could not be set to correct lenght.");
                        if command == "wq".to_string() {
                            quitnow = 1;
                        }
                        command.clear();
                        mode = Mode::Command;
                    } else if command == "q".to_string() {
                        quitnow = 1;
                    } else {
                        command.clear();
                        command.push_str("Bad_command!");
                        mode = Mode::Command;
                    }
                }
                _ => (),
            }
        } else if mode == Mode::Insert {
            if cursorstate == 0 {
                // Left nibble
                if let Some(c) = (key as char).to_digit(16) {
                    buf.insert(cursorpos, (c as u8) << 4); cursorstate = 1;
                }
                if key == 27 {mode = Mode::Command;};
            } else if cursorstate == 1 {
                // Right nibble
                if cursorpos == buf.len() {
                    buf.insert(cursorpos, 0 );
                }
                if let Some(c) = (key as char).to_digit(16) {
                    buf[cursorpos] = buf[cursorpos]&0xF0 | c as u8; cursorstate = 0; cursorpos+=1;
                }
                if key == 27 {mode = Mode::Command;};
            } else if cursorstate == 2 {
                // Ascii
                match key {
                    c @ 32...126 => {
                        buf.insert(cursorpos, c);
                        cursorpos += 1;
                    }
                    27 => {
                        mode = Mode::Command;
                    }
                    _ => (),
                }
            }
        } else if mode == Mode::TypeSearch {
            //            if cursorstate == 2 { // Ascii search
            match key {
                c @ 32...126 => {
                    command.push(c as char);
                }
                27 => {
                    command.clear();
                    mode = Mode::Command;
                }
                10 => {
                    // Enter pressed, compute command!
                    if cursorstate == 2 {
                        mode = Mode::SearchIt;
                    } else {
                        mode = Mode::SearchItHex;
                    }
                }
                _ => (),
            }
            //            }

            if mode == Mode::SearchIt {
                let search = command.as_bytes();
                cursorpos = buf.find_subset(&search).unwrap_or(cursorpos);
                mode = Mode::Command;
                // command.push_str("Bad_command!");
            }
            if mode == Mode::SearchItHex {
                // TODO
                mode = Mode::Command;
                command.push_str("Hex search not yet implemented!");
            }
        }
        draw(&buf,
             cursorpos,
             SPALTEN,
             mode,
             &command,
             cursorstate,
             screenoffset);
    }

    refresh();
    endwin();
}
<|endoftext|>


<commit_before>#![feature(conservative_impl_trait)]

extern crate curl;
extern crate env_logger;
extern crate futures;
extern crate tokio_core;
extern crate tokio_curl;

#[macro_use]
extern crate clap;
extern crate chrono;
extern crate histogram;

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

mod args;
mod misc;

use args::{Config, parse_args};

use curl::easy::Easy;
use futures::{Future, stream};
use futures::future::{ok, loop_fn, Loop};
use futures::Stream;
use tokio_core::reactor::Core;
use tokio_curl::{Session, PerformError};
use chrono::*;
use std::sync::mpsc::channel;
use histogram::*;
use std::thread;

struct RequestResult {
    latency: Duration,
}

impl RequestResult {
    fn new(latency: Duration) -> Self {
        RequestResult { latency: latency }
    }
}

impl Default for RequestResult {
    fn default() -> Self {
        RequestResult { latency: Duration::zero() }
    }
}

fn send_request(easy_request: Easy,
                session: &Session)
                -> impl Future<Item = (RequestResult, Easy), Error = PerformError> {
    session.perform(easy_request)
        .and_then(|mut easy| {
            let latency = easy.total_time()
                .ok()
                .and_then(|x| Duration::from_std(x).ok())
                .unwrap();
            let request_result = RequestResult::new(latency);
            Ok((request_result, easy))
        })
}

struct RunInfo {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    pub duration: Duration,
    histogram: Histogram,
    /// Number of failed requests
    num_failed_requests: usize,
}

impl RunInfo {
    fn new(duration: Duration) -> Self {
        RunInfo {
            requests_completed: 0,
            num_failed_requests: 0,
            duration: duration,
            histogram: Histogram::new(),
        }
    }
    pub fn requests_per_second(&self) -> f32 {
        (self.requests_completed as f32) / (self.duration.num_seconds() as f32)
    }

    fn merge(&mut self, other: &Self) {
        self.requests_completed += other.requests_completed;
        self.num_failed_requests += other.num_failed_requests;
        self.histogram.merge(&other.histogram);
    }
}

/// Delegates and manages all the work.
struct Boss {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    /// Number of active connections
    pub connections: usize,
    num_threads: usize,
}

fn make_easy(url: &str, timeout: Duration) -> Easy {
    let mut easy_request = Easy::new();
    easy_request.get(true).unwrap();
    easy_request.url(&url).unwrap();
    easy_request.timeout(timeout.to_std().unwrap()).unwrap();
    easy_request
}

impl Boss {
    pub fn new(num_threads: usize) -> Self {
        Boss {
            requests_completed: 0,
            connections: 0,
            num_threads: num_threads,
        }
    }

    pub fn start_workforce(&self,
                           desired_connections: usize,
                           url: String,
                           duration: Duration,
                           timeout: Duration)
                           -> RunInfo {
        let (tx, rx) = channel();
        let desired_connections_per_worker_iter = misc::split_number(desired_connections,
                                                                     self.num_threads);
        // start num_threads workers
        for desired_connections_per_worker in desired_connections_per_worker_iter {
            let tx = tx.clone();
            let url = url.clone();
            thread::spawn(move || {
                let mut lp = Core::new().unwrap();
                let start_time = Local::now();
                let wanted_end_time = start_time + duration;
                let session = Session::new(lp.handle());

                let iterator = (0..desired_connections_per_worker).map(|_| {

                    let runinfo = RunInfo::new(duration);
                    let easy_request = make_easy(&url, timeout);
                    loop_fn((runinfo, easy_request), |(mut runinfo, easy_request)| {
                        send_request(easy_request, &session)
                            .then(|res| -> Result<_, ()> { match res {
                                // on request success
                                Ok((request_res, easy_request)) => {
                                    // update histogram
                                    request_res.latency.num_nanoseconds()
                                        .map(|x| runinfo.histogram.increment(x as u64).ok());
                                    runinfo.requests_completed += 1;

                                    let state = (runinfo, easy_request);

                                    let now_time = Local::now();
                                    if now_time < wanted_end_time {
                                        Ok(Loop::Continue(state))
                                    } else {
                                        Ok(Loop::Break(state))
                                    }
                                }
                                // on request failure
                                Err(mut err) => {
                                    runinfo.num_failed_requests += 1;
                                    // attempt to recover the easy handle from the error,
                                    // else make a new handle
                                    let easy_request = err.take_easy().unwrap_or(make_easy(&url, timeout));
                                    let state = (runinfo, easy_request);
                                    let now_time = Local::now();
                                    if now_time < wanted_end_time {
                                        Ok(Loop::Continue(state))
                                    } else {
                                        Ok(Loop::Break(state))
                                    }
                                }
                            }})
                            .map_err(|_| ())
                    })
                        // Extract latency histogram
                        .map(|(histogram, _)| histogram)
                });
                let future = stream::futures_unordered(iterator)
                    .fold(RunInfo::new(duration), |mut runinfo_acc, runinfo| {
                        runinfo_acc.merge(&runinfo);
                        ok(runinfo_acc)
                    });
                let res = lp.run(future).unwrap();
                tx.send(res).unwrap();
            });
        }
        // collect information from all workers
        rx.iter()
            .take(self.num_threads)
            .fold(RunInfo::new(duration), |mut runinfo_acc, runinfo| {
                runinfo_acc.merge(&runinfo);
                runinfo_acc
            })
    }
}

fn start(config: Config) -> RunInfo {
    let boss = Boss::new(config.num_threads);
    boss.start_workforce(config.num_connections, config.url, config.duration, config.timeout)
}

fn nanoseconds_to_milliseconds(nanoseconds: u64) -> f64 {
    nanoseconds as f64 / 1_000_000_f64
}

/// Presents the results to the user
fn present(run_info: RunInfo) {
    let requests_completed = run_info.requests_completed;
    println!("{} requests in {}s",
             requests_completed,
             run_info.duration.num_seconds());
    println!("{} failed requests", run_info.num_failed_requests);
    println!("Requests per second: {}", run_info.requests_per_second());

    // we can only print the latency distribution if there is data in the histogram
    if run_info.histogram.entries() > 0 {
        println!("Latency distribution: \n50%: {} ms\n75%: {} ms\n90%: {} ms\n95%: {} ms\n99%: {} ms",
            run_info.histogram.percentile(50.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(75.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(90.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(95.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(99.0).map(nanoseconds_to_milliseconds).unwrap(),
        );
    }

}

fn main() {
    env_logger::init().unwrap();
    let config = parse_args();
    let run_info = start(config);
    present(run_info)
}
<commit_msg>refactor: move config around rather than constituents<commit_after>#![feature(conservative_impl_trait)]

extern crate curl;
extern crate env_logger;
extern crate futures;
extern crate tokio_core;
extern crate tokio_curl;

#[macro_use]
extern crate clap;
extern crate chrono;
extern crate histogram;

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

mod args;
mod misc;

use args::{Config, parse_args};

use curl::easy::Easy;
use futures::{Future, stream};
use futures::future::{ok, loop_fn, Loop};
use futures::Stream;
use tokio_core::reactor::Core;
use tokio_curl::{Session, PerformError};
use chrono::*;
use std::sync::mpsc::channel;
use histogram::*;
use std::thread;

struct RequestResult {
    latency: Duration,
}

impl RequestResult {
    fn new(latency: Duration) -> Self {
        RequestResult { latency: latency }
    }
}

impl Default for RequestResult {
    fn default() -> Self {
        RequestResult { latency: Duration::zero() }
    }
}

fn send_request(easy_request: Easy,
                session: &Session)
                -> impl Future<Item = (RequestResult, Easy), Error = PerformError> {
    session.perform(easy_request)
        .and_then(|mut easy| {
            let latency = easy.total_time()
                .ok()
                .and_then(|x| Duration::from_std(x).ok())
                .unwrap();
            let request_result = RequestResult::new(latency);
            Ok((request_result, easy))
        })
}

struct RunInfo {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    pub duration: Duration,
    histogram: Histogram,
    /// Number of failed requests
    num_failed_requests: usize,
}

impl RunInfo {
    fn new(duration: Duration) -> Self {
        RunInfo {
            requests_completed: 0,
            num_failed_requests: 0,
            duration: duration,
            histogram: Histogram::new(),
        }
    }
    pub fn requests_per_second(&self) -> f32 {
        (self.requests_completed as f32) / (self.duration.num_seconds() as f32)
    }

    fn merge(&mut self, other: &Self) {
        self.requests_completed += other.requests_completed;
        self.num_failed_requests += other.num_failed_requests;
        self.histogram.merge(&other.histogram);
    }
}

/// Delegates and manages all the work.
struct Boss {
    /// Number of requests successfully completed
    pub requests_completed: usize,
    /// Number of active connections
    pub connections: usize,
    num_threads: usize,
}

fn make_easy(url: &str, timeout: Duration) -> Easy {
    let mut easy_request = Easy::new();
    easy_request.get(true).unwrap();
    easy_request.url(&url).unwrap();
    easy_request.timeout(timeout.to_std().unwrap()).unwrap();
    easy_request
}

impl Boss {
    pub fn new(num_threads: usize) -> Self {
        Boss {
            requests_completed: 0,
            connections: 0,
            num_threads: num_threads,
        }
    }

    pub fn start_workforce(&self, config: &Config) -> RunInfo {
        let (tx, rx) = channel();
        let desired_connections_per_worker_iter = misc::split_number(config.num_connections,
                                                                     self.num_threads);
        // start num_threads workers
        for desired_connections_per_worker in desired_connections_per_worker_iter {
            let tx = tx.clone();
            let url = config.url.clone();
            let duration = config.duration;
            let timeout = config.timeout;
            thread::spawn(move || {

                let mut lp = Core::new().unwrap();
                let start_time = Local::now();
                let wanted_end_time = start_time + duration;
                let session = Session::new(lp.handle());

                let iterator = (0..desired_connections_per_worker).map(|_| {

                    let runinfo = RunInfo::new(duration);
                    let easy_request = make_easy(&url, timeout);
                    loop_fn((runinfo, easy_request), |(mut runinfo, easy_request)| {
                        send_request(easy_request, &session)
                            .then(|res| -> Result<_, ()> { match res {
                                // on request success
                                Ok((request_res, easy_request)) => {
                                    // update histogram
                                    request_res.latency.num_nanoseconds()
                                        .map(|x| runinfo.histogram.increment(x as u64).ok());
                                    runinfo.requests_completed += 1;

                                    let state = (runinfo, easy_request);

                                    let now_time = Local::now();
                                    if now_time < wanted_end_time {
                                        Ok(Loop::Continue(state))
                                    } else {
                                        Ok(Loop::Break(state))
                                    }
                                }
                                // on request failure
                                Err(mut err) => {
                                    runinfo.num_failed_requests += 1;
                                    // attempt to recover the easy handle from the error,
                                    // else make a new handle
                                    let easy_request = err.take_easy().unwrap_or(make_easy(&url, timeout));
                                    let state = (runinfo, easy_request);
                                    let now_time = Local::now();
                                    if now_time < wanted_end_time {
                                        Ok(Loop::Continue(state))
                                    } else {
                                        Ok(Loop::Break(state))
                                    }
                                }
                            }})
                            .map_err(|_| ())
                    })
                        // Extract latency histogram
                        .map(|(histogram, _)| histogram)
                });
                let future = stream::futures_unordered(iterator)
                    .fold(RunInfo::new(duration), |mut runinfo_acc, runinfo| {
                        runinfo_acc.merge(&runinfo);
                        ok(runinfo_acc)
                    });
                let res = lp.run(future).unwrap();
                tx.send(res).unwrap();
            });
        }
        // collect information from all workers
        rx.iter()
            .take(self.num_threads)
            .fold(RunInfo::new(config.duration), |mut runinfo_acc, runinfo| {
                runinfo_acc.merge(&runinfo);
                runinfo_acc
            })
    }
}

fn start(config: Config) -> RunInfo {
    let boss = Boss::new(config.num_threads);
    boss.start_workforce(&config)
}

fn nanoseconds_to_milliseconds(nanoseconds: u64) -> f64 {
    nanoseconds as f64 / 1_000_000_f64
}

/// Presents the results to the user
fn present(run_info: RunInfo) {
    let requests_completed = run_info.requests_completed;
    println!("{} requests in {}s",
             requests_completed,
             run_info.duration.num_seconds());
    println!("{} failed requests", run_info.num_failed_requests);
    println!("Requests per second: {}", run_info.requests_per_second());

    // we can only print the latency distribution if there is data in the histogram
    if run_info.histogram.entries() > 0 {
        println!("Latency distribution: \n50%: {} ms\n75%: {} ms\n90%: {} ms\n95%: {} ms\n99%: {} ms",
            run_info.histogram.percentile(50.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(75.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(90.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(95.0).map(nanoseconds_to_milliseconds).unwrap(),
            run_info.histogram.percentile(99.0).map(nanoseconds_to_milliseconds).unwrap(),
        );
    }

}

fn main() {
    env_logger::init().unwrap();
    let config = parse_args();
    let run_info = start(config);
    present(run_info)
}
<|endoftext|>


<commit_before>extern crate ansi_term;
#[macro_use]
extern crate clap;
extern crate hyper;
extern crate libsnatch;
extern crate num_cpus;

use ansi_term::Colour::{Green, Yellow, Red};
use clap::{App, Arg};
use libsnatch::download::download_chunks;
use libsnatch::write::OutputFileWriter;
use libsnatch::util::prompt_user;
use libsnatch::filesize::format_filesize;
use libsnatch::cargo_helper::get_cargo_info;
use std::fs::{File, remove_file};
use std::path::Path;
use std::process::exit;

static DEFAULT_FILENAME: &'static str = "index.html";

fn main() {

    // Parse arguments

    let argparse = App::new("Snatch")
        .about("Snatch, a simple, fast and interruptable download accelerator, written in Rust.")
        .version(crate_version!())
        .arg(Arg::with_name("file")
            .long("file")
            .short("f")
            .takes_value(true)
            .help("The local file to save the remote content file"))
        .arg(Arg::with_name("threads")
            .long("threads")
            .short("t")
            .takes_value(true)
            .help("Threads which can use to download"))
        .arg(Arg::with_name("debug")
            .long("debug")
            .short("d")
            .help("Active the debug mode"))
        .arg(Arg::with_name("force")
            .long("force")
            .help("Assume Yes to all queries and do not prompt"))
        .arg(Arg::with_name("url")
            .index(1)
            //.multiple(true)
            .required(true))
        .get_matches();

    // Get informations from arguments

    let url = argparse.value_of("url").unwrap();

    let file = argparse.value_of("file").unwrap_or_else(|| {
                                                            url.split('/')
                                                                .last()
                                                                .unwrap_or(DEFAULT_FILENAME)
                                                        });

    let threads: usize = value_t!(argparse, "threads", usize).unwrap_or(num_cpus::get_physical());

    if argparse.is_present("debug") {
        println!("# [{}] version: {}",
                 Yellow.bold().paint("DEBUG_MODE"),
                 crate_version!());
        println!("# [{}] file: {}", Yellow.bold().paint("DEBUG_MODE"), file);
        println!("# [{}] threads: {}",
                 Yellow.bold().paint("DEBUG_MODE"),
                 threads);
    }

    let local_path = Path::new(&file);

    if local_path.exists() {
        if local_path.is_dir() {
            panic!(Red.bold()
                .paint("[ERROR] The local path to store the remote content is already exists, \
                        and is a directory!"));
        }
        if !argparse.is_present("force") {
            let user_input = prompt_user(Yellow.bold(),
                                         "[WARNING] The path to store the file already exists! \
                                          Do you want to override it? [y/N]");

            if !(user_input == "y" || user_input == "Y") {
                exit(0);
            }
        } else {
            println!("{}",
                     Yellow.bold().paint("[WARNING] The path to store the file already exists! \
                                 It is going to be overriden."));
        }
    }

    let cargo_info = get_cargo_info(&url).expect("fail to parse url");
    println!("# Remote content length: {}",
             format_filesize(cargo_info.content_length));

    let local_file = File::create(local_path).expect("[ERROR] Cannot create a file !");

    local_file.set_len(cargo_info.content_length)
        .expect("[ERROR] Cannot extend file to download size!");
    let out_file = OutputFileWriter::new(local_file);

    if download_chunks(cargo_info, out_file, threads as u64, &url) {
        println!("{} Your download is available in {}",
                Green.bold().paint("Done!"),
                local_path.to_str().unwrap());
    } else {
        // If the file is not ok, delete it from the file system
        print!("{} An error occured - erasing file... ",
                Red.bold().paint("Failed!"));
        match remove_file(local_path) {
            Ok(_) => println!("done !"),
            Err(e) => println!("failed ({}) !", e)
        }
    }

}
<commit_msg>Format code<commit_after>extern crate ansi_term;
#[macro_use]
extern crate clap;
extern crate hyper;
extern crate libsnatch;
extern crate num_cpus;

use ansi_term::Colour::{Green, Yellow, Red};
use clap::{App, Arg};
use libsnatch::download::download_chunks;
use libsnatch::write::OutputFileWriter;
use libsnatch::util::prompt_user;
use libsnatch::filesize::format_filesize;
use libsnatch::cargo_helper::get_cargo_info;
use std::fs::{File, remove_file};
use std::path::Path;
use std::process::exit;

static DEFAULT_FILENAME: &'static str = "index.html";

fn main() {

    // Parse arguments

    let argparse = App::new("Snatch")
        .about("Snatch, a simple, fast and interruptable download accelerator, written in Rust.")
        .version(crate_version!())
        .arg(Arg::with_name("file")
                 .long("file")
                 .short("f")
                 .takes_value(true)
                 .help("The local file to save the remote content file"))
        .arg(Arg::with_name("threads")
                 .long("threads")
                 .short("t")
                 .takes_value(true)
                 .help("Threads which can use to download"))
        .arg(Arg::with_name("debug")
                 .long("debug")
                 .short("d")
                 .help("Active the debug mode"))
        .arg(Arg::with_name("force")
                 .long("force")
                 .help("Assume Yes to all queries and do not prompt"))
        .arg(Arg::with_name("url")
            .index(1)
            //.multiple(true)
            .required(true))
        .get_matches();

    // Get informations from arguments

    let url = argparse.value_of("url").unwrap();

    let file = argparse
        .value_of("file")
        .unwrap_or_else(|| url.split('/').last().unwrap_or(DEFAULT_FILENAME));

    let threads: usize = value_t!(argparse, "threads", usize).unwrap_or(num_cpus::get_physical());

    if argparse.is_present("debug") {
        println!("# [{}] version: {}",
                 Yellow.bold().paint("DEBUG_MODE"),
                 crate_version!());
        println!("# [{}] file: {}", Yellow.bold().paint("DEBUG_MODE"), file);
        println!("# [{}] threads: {}",
                 Yellow.bold().paint("DEBUG_MODE"),
                 threads);
    }

    let local_path = Path::new(&file);

    if local_path.exists() {
        if local_path.is_dir() {
            panic!(Red.bold()
                .paint("[ERROR] The local path to store the remote content is already exists, \
                        and is a directory!"));
        }
        if !argparse.is_present("force") {
            let user_input = prompt_user(Yellow.bold(),
                                         "[WARNING] The path to store the file already exists! \
                                          Do you want to override it? [y/N]");

            if !(user_input == "y" || user_input == "Y") {
                exit(0);
            }
        } else {
            println!("{}",
                     Yellow
                         .bold()
                         .paint("[WARNING] The path to store the file already exists! \
                                 It is going to be overriden."));
        }
    }

    let cargo_info = get_cargo_info(&url).expect("fail to parse url");
    println!("# Remote content length: {}",
             format_filesize(cargo_info.content_length));

    let local_file = File::create(local_path).expect("[ERROR] Cannot create a file !");

    local_file
        .set_len(cargo_info.content_length)
        .expect("[ERROR] Cannot extend file to download size!");
    let out_file = OutputFileWriter::new(local_file);

    if download_chunks(cargo_info, out_file, threads as u64, &url) {
        println!("{} Your download is available in {}",
                 Green.bold().paint("Done!"),
                 local_path.to_str().unwrap());
    } else {
        // If the file is not ok, delete it from the file system
        print!("{} An error occured - erasing file... ",
               Red.bold().paint("Failed!"));
        match remove_file(local_path) {
            Ok(_) => println!("done !"),
            Err(e) => println!("failed ({}) !", e),
        }
    }

}
<|endoftext|>


<commit_before>#![allow(unstable)]

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex_macros;
extern crate regex;

use std::os;
use std::old_io;
use todo::list::List;
use query::Query;
use query::QueryScope;

mod query;
pub mod todo {
    pub mod list;
    pub mod entry;
}

fn main() {
    let mut path = os::getcwd().unwrap();
    let mut args = os::args();
    args.remove(0);

    path.push("todo.txt");

    match args.len() {
        0 => render_help(),
        _ => dispatch(path, Query::new(args))
    }
}

fn dispatch(path: Path, query: Query) {
    let mut list = List::new(path);
    list.load();

    match query.action.as_slice() {
        "list" => list.index(query.scope.to_string()),
        "add" => {
            let content = match query.scope.to_string() {
                Some(val) => val,
                None => read_input("Write a description for your task:")
            };
            list.add(content);
            list.save();
        },
        "edit" => {
            let id = match query.scope.to_vec_int() {
                Some(val) => val[0],
                None => {
                    let input = read_input("What's the id of the task that you want to edit?");
                    let parsed_input: Result<usize, _> = input.parse::<usize>();
                    parsed_input.unwrap()
                }
            };
            list.edit(id, read_input("Write a description for your task:"));
            list.save();
        },
        "remove" => {
            let ids = match query.scope.to_vec_int() {
                Some(val) => val,
                None => {
                    let input = read_input("Enter the ids of the entries you want to remove:");
                    QueryScope::new(split_string(input)).to_vec_int().unwrap()
                }
            };
            list.remove(ids);
            list.save();
        },
        "finish" => {
            let ids = match query.scope.to_vec_int() {
                Some(val) => val,
                None => {
                    let input = read_input("Enter the ids of the entries you want to remove:");
                    QueryScope::new(split_string(input)).to_vec_int().unwrap()
                }
            };
            list.finish(ids);
            list.save();
        },
        "count" => list.count(query.scope.to_string()),
        _ => println!("There is no action named `{}`.", query.action)
    }
}

fn read_input(greeting: &str) -> String {
    println!("{}", greeting);
    old_io::stdin().read_line().unwrap().trim().to_string()
}

fn split_string(value: String) -> Vec<String> {
    value.split_str(" ")
         .map(|s| s.to_string())
         .collect()
}

fn render_help() {
    println!("Print help");
}
<commit_msg>Use io namespace over old_io<commit_after>#![allow(unstable)]

#![feature(plugin)]
#![plugin(regex_macros)]
extern crate regex_macros;
extern crate regex;

use std::os;
use std::old_io as io;
use todo::list::List;
use query::Query;
use query::QueryScope;

mod query;
pub mod todo {
    pub mod list;
    pub mod entry;
}

fn main() {
    let mut path = os::getcwd().unwrap();
    let mut args = os::args();
    args.remove(0);

    path.push("todo.txt");

    match args.len() {
        0 => render_help(),
        _ => dispatch(path, Query::new(args))
    }
}

fn dispatch(path: Path, query: Query) {
    let mut list = List::new(path);
    list.load();

    match query.action.as_slice() {
        "list" => list.index(query.scope.to_string()),
        "add" => {
            let content = match query.scope.to_string() {
                Some(val) => val,
                None => read_input("Write a description for your task:")
            };
            list.add(content);
            list.save();
        },
        "edit" => {
            let id = match query.scope.to_vec_int() {
                Some(val) => val[0],
                None => {
                    let input = read_input("What's the id of the task that you want to edit?");
                    let parsed_input: Result<usize, _> = input.parse::<usize>();
                    parsed_input.unwrap()
                }
            };
            list.edit(id, read_input("Write a description for your task:"));
            list.save();
        },
        "remove" => {
            let ids = match query.scope.to_vec_int() {
                Some(val) => val,
                None => {
                    let input = read_input("Enter the ids of the entries you want to remove:");
                    QueryScope::new(split_string(input)).to_vec_int().unwrap()
                }
            };
            list.remove(ids);
            list.save();
        },
        "finish" => {
            let ids = match query.scope.to_vec_int() {
                Some(val) => val,
                None => {
                    let input = read_input("Enter the ids of the entries you want to remove:");
                    QueryScope::new(split_string(input)).to_vec_int().unwrap()
                }
            };
            list.finish(ids);
            list.save();
        },
        "count" => list.count(query.scope.to_string()),
        _ => println!("There is no action named `{}`.", query.action)
    }
}

fn read_input(greeting: &str) -> String {
    println!("{}", greeting);
    io::stdin().read_line().unwrap().trim().to_string()
}

fn split_string(value: String) -> Vec<String> {
    value.split_str(" ")
         .map(|s| s.to_string())
         .collect()
}

fn render_help() {
    println!("Print help");
}
<|endoftext|>


<commit_before>// setup limit for error-chain
#![recursion_limit = "1024"]

// #[macro_use] extern crate error_chain;
#[macro_use] extern crate serde_derive;
extern crate chrono;
extern crate serde;
extern crate serde_json;
extern crate ttml;

use chrono::DateTime;
use chrono::prelude::Utc;
use std::ffi::CString;
use std::ffi::CStr;
use std::os::raw::c_char;
use ttml::die::Die;
// use ttml::parser::execute_ast;
// use ttml::parser::parse_ttml;
use ttml::token::Token;

pub fn main() {
    // IGNORE ME!
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Output {
    /// The original input
    pub input: String,

    /// Timestamp
    pub executed: DateTime<Utc>,

    /// Time to execute final output
    pub execution_time: i64,

    /// Chat messages to be sent
    pub messages: Vec<String>,

    /// Dice rolls
    pub rolls: Vec<Die>,

    /// Tokens
    pub tokens: Vec<Token>,

    /// API Version
    pub version: String,
}

pub fn safe_string(input: *mut c_char) -> String {
    unsafe {
        CStr::from_ptr(input).to_string_lossy().into_owned()
    }
}

/// Run input and return a typed array for use in javascript
#[no_mangle]
pub fn parse(raw_input: *mut c_char) -> *mut c_char {
    let input = safe_string(raw_input);
    let output = Output {
        input,
        executed: Utc::now(),
        execution_time: 0,
        messages: Vec::new(),
        rolls: Vec::new(),
        tokens: Vec::new(),
        version: String::from("0.1.0"),
    };

    let json = serde_json::to_string(&output).unwrap();
    CString::new(json).unwrap().into_raw()
}

#[test]
fn it_parses_input() {
    let chars = CString::new("#test!say \"Hello\"").unwrap().into_raw();
    let raw_output = parse(chars);
    let json = safe_string(raw_output);
    let output: Output = serde_json::from_str(&json).unwrap();

    assert_eq!(output.input, "#test!say \"Hello\"");
    assert_eq!(output.version, "0.1.0");
}
<commit_msg>formatting for errors<commit_after>// setup limit for error-chain
#![recursion_limit = "1024"]

// #[macro_use] extern crate error_chain;
#[macro_use] extern crate serde_derive;
extern crate chrono;
extern crate serde;
extern crate serde_json;
extern crate ttml;

use chrono::DateTime;
use chrono::prelude::Utc;
use std::ffi::CString;
use std::ffi::CStr;
use std::os::raw::c_char;
use ttml::die::Die;
// use ttml::parser::execute_ast;
// use ttml::parser::parse_ttml;
use ttml::token::Token;

pub fn main() {
    // IGNORE ME!
}

#[derive(Debug, Deserialize, Serialize)]
pub struct ErrorOutput {
    /// Type of error
    error: String,

    /// Message
    message: String
}

#[derive(Debug, Deserialize, Serialize)]
pub struct Output {
    /// The original input
    pub input: String,

    /// Errors, if any
    pub errors: Vec<ErrorOutput>,

    /// Timestamp
    pub executed: DateTime<Utc>,

    /// Time to execute final output
    pub execution_time: i64,

    /// Chat messages to be sent
    pub messages: Vec<String>,

    /// Dice rolls
    pub rolls: Vec<Die>,

    /// Tokens
    pub tokens: Vec<Token>,

    /// API Version
    pub version: String,
}

pub fn safe_string(input: *mut c_char) -> String {
    unsafe {
        CStr::from_ptr(input).to_string_lossy().into_owned()
    }
}

/// Run input and return a typed array for use in javascript
#[no_mangle]
pub fn parse(raw_input: *mut c_char) -> *mut c_char {
    // Take the input and safely covert it to a String
    let input = safe_string(raw_input);
    // Parse the ast from the macro, if we get any errors we will exit and throw a custom error
    let output = Output {
        input,
        executed: Utc::now(),
        execution_time: 0,
        messages: Vec::new(),
        errors: Vec::new(),
        rolls: Vec::new(),
        tokens: Vec::new(),
        version: String::from("0.1.0"),
    };

    let json = serde_json::to_string(&output).unwrap();
    CString::new(json).unwrap().into_raw()
}

#[test]
fn it_parses_input() {
    let chars = CString::new("#test!say \"Hello\"").unwrap().into_raw();
    let raw_output = parse(chars);
    let json = safe_string(raw_output);
    let output: Output = serde_json::from_str(&json).unwrap();

    assert_eq!(output.input, "#test!say \"Hello\"");
    assert_eq!(output.version, "0.1.0");
}
<|endoftext|>


<commit_before>extern crate regex;
use regex::Regex;

use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;
extern crate time;
use time::Tm;
use time::strptime;
use time::Duration;

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub enum HttpStatus {
    Continue,
    SwitchingProtocols,
    Processing,
    OK,
    Created,
    Accepted,
    NonAuthoritativeInformation,
    NoContent,
    ResetContent,
    PartialContent,
    MultiStatus,
    AlreadyReported,
    IMUsed,
    MultipleChoices,
    MovedPermanently,
    Found,
    SeeOther,
    NotModified,
    UseProxy,
    TemporaryRedirect,
    PermanentRedirect,
    BadRequest,
    Unauthorized,
    PaymentRequired,
    Forbidden,
    NotFound,
    MethodNotAllowed,
    NotAcceptable,
    ProxyAuthenticationRequired,
    RequestTimeout,
    Conflict,
    Gone,
    LengthRequired,
    PreconditionFailed,
    PayloadTooLarge,
    URITooLong,
    UnsupportedMediaType,
    RangeNotSatisfiable,
    ExpectationFailed,
    ImaTeapot,
    MisdirectedRequest,
    UnprocessableEntity,
    Locked,
    FailedDependency,
    UpgradeRequired,
    PreconditionRequired,
    TooManyRequests,
    RequestHeaderFieldsTooLarge,
    UnavailableForLegalReasons,
    InternalServerError,
    NotImplemented,
    BadGateway,
    ServiceUnavailable,
    GatewayTimeout,
    HTTPVersionNotSupported,
    VariantAlsoNegotiates,
    InsufficientStorage,
    LoopDetected,
    NotExtended,
    NetworkAuthenticationRequired,
    Unregistered,
}

impl HttpStatus {
    fn from_code(code: i32) -> HttpStatus {
        match code {
            100 => HttpStatus::Continue,
            101 => HttpStatus::SwitchingProtocols,
            102 => HttpStatus::Processing,
            200 => HttpStatus::OK,
            201 => HttpStatus::Created,
            202 => HttpStatus::Accepted,
            203 => HttpStatus::NonAuthoritativeInformation,
            204 => HttpStatus::NoContent,
            205 => HttpStatus::ResetContent,
            206 => HttpStatus::PartialContent,
            207 => HttpStatus::MultiStatus,
            208 => HttpStatus::AlreadyReported,
            226 => HttpStatus::IMUsed,
            300 => HttpStatus::MultipleChoices,
            301 => HttpStatus::MovedPermanently,
            302 => HttpStatus::Found,
            303 => HttpStatus::SeeOther,
            304 => HttpStatus::NotModified,
            305 => HttpStatus::UseProxy,
            307 => HttpStatus::TemporaryRedirect,
            308 => HttpStatus::PermanentRedirect,
            400 => HttpStatus::BadRequest,
            401 => HttpStatus::Unauthorized,
            402 => HttpStatus::PaymentRequired,
            403 => HttpStatus::Forbidden,
            404 => HttpStatus::NotFound,
            405 => HttpStatus::MethodNotAllowed,
            406 => HttpStatus::NotAcceptable,
            407 => HttpStatus::ProxyAuthenticationRequired,
            408 => HttpStatus::RequestTimeout,
            409 => HttpStatus::Conflict,
            410 => HttpStatus::Gone,
            411 => HttpStatus::LengthRequired,
            412 => HttpStatus::PreconditionFailed,
            413 => HttpStatus::PayloadTooLarge,
            414 => HttpStatus::URITooLong,
            415 => HttpStatus::UnsupportedMediaType,
            416 => HttpStatus::RangeNotSatisfiable,
            417 => HttpStatus::ExpectationFailed,
            418 => HttpStatus::ImaTeapot,
            421 => HttpStatus::MisdirectedRequest,
            422 => HttpStatus::UnprocessableEntity,
            423 => HttpStatus::Locked,
            424 => HttpStatus::FailedDependency,
            426 => HttpStatus::UpgradeRequired,
            428 => HttpStatus::PreconditionRequired,
            429 => HttpStatus::TooManyRequests,
            431 => HttpStatus::RequestHeaderFieldsTooLarge,
            451 => HttpStatus::UnavailableForLegalReasons,
            500 => HttpStatus::InternalServerError,
            501 => HttpStatus::NotImplemented,
            502 => HttpStatus::BadGateway,
            503 => HttpStatus::ServiceUnavailable,
            504 => HttpStatus::GatewayTimeout,
            505 => HttpStatus::HTTPVersionNotSupported,
            506 => HttpStatus::VariantAlsoNegotiates,
            507 => HttpStatus::InsufficientStorage,
            508 => HttpStatus::LoopDetected,
            510 => HttpStatus::NotExtended,
            511 => HttpStatus::NetworkAuthenticationRequired,
            _ => HttpStatus::Unregistered,
        }
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Request {
    id: i32,
    time: Tm,
    url: String,
}

impl Request {
    fn get_matching_response<'a>(&'a self, responses: &'a Vec<Response>) -> Option<&'a Response> {
        for response in responses {
            if self.id == response.id {
                return Some(&response)
            }
        }

        None
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Response {
    id: i32,
    time: Tm,
    mime_type: String,
    response_time: Duration,
    http_status: HttpStatus,
}

pub fn open_logfile(path: &str) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(parse_request_line(&line_value));
            println!("{:#?}", r);
            requests.push(r)
        }

        if line_value.contains("<-") {
            let r = try!(parse_response_line(&line_value));
            println!("{:#?}", r);
            responses.push(r)
        }

    }

    Ok((requests, responses))
}

pub fn parse_request_line(log_line: &String) -> Result<Request, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let url = parts[5];

    Ok(Request {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        url: url.to_string()
    })
}

pub fn parse_response_line(log_line: &String) -> Result<Response, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let response_time = parts[parts.len()-1];
    let mime_type = parts[5];
    let status_code = parts[4];

    Ok(Response {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        response_time: Duration::milliseconds(response_time[0..response_time.len()-2].parse().unwrap()),
        mime_type: mime_type.to_string(),
        http_status: HttpStatus::from_code(status_code.parse().unwrap()),
    })
}

fn main() {
    let lines = open_logfile("src/test/simple-1.log");
    let (requests, responses) = lines.unwrap();
    println!("So many requests: {}", requests.len());
    println!("So many responses: {}", responses.len());
}

#[cfg(test)]
mod tests {
	use super::*;
    extern crate time;
    use time::strptime;
    use::time::Duration;

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();

        let expected = Request {
            id: 2,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = parse_request_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            time: strptime("08/Apr/2016:09:58:48 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(10),
            http_status: HttpStatus::OK,
        };

        let result = parse_response_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_get_matching_response() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let result = requests[0].get_matching_response(&responses);

        let expected = Response {
            id: 1,
            time: strptime("08/Apr/2016:09:57:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(7),
            http_status: HttpStatus::OK,
        };

        assert_eq!(*result.unwrap(), expected);
    }

    #[test]
    fn test_get_matching_response_none_found() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let request_without_matching = Request {
            id: 999,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = request_without_matching.get_matching_response(&responses);

        assert!(result.is_none());
    }
}
<commit_msg>Matching Request/Response pairs, return references<commit_after>extern crate regex;
use regex::Regex;

use std::io::{self, BufReader};
use std::io::BufRead;
use std::fs::File;
extern crate time;
use time::Tm;
use time::strptime;
use time::Duration;

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub enum HttpStatus {
    Continue,
    SwitchingProtocols,
    Processing,
    OK,
    Created,
    Accepted,
    NonAuthoritativeInformation,
    NoContent,
    ResetContent,
    PartialContent,
    MultiStatus,
    AlreadyReported,
    IMUsed,
    MultipleChoices,
    MovedPermanently,
    Found,
    SeeOther,
    NotModified,
    UseProxy,
    TemporaryRedirect,
    PermanentRedirect,
    BadRequest,
    Unauthorized,
    PaymentRequired,
    Forbidden,
    NotFound,
    MethodNotAllowed,
    NotAcceptable,
    ProxyAuthenticationRequired,
    RequestTimeout,
    Conflict,
    Gone,
    LengthRequired,
    PreconditionFailed,
    PayloadTooLarge,
    URITooLong,
    UnsupportedMediaType,
    RangeNotSatisfiable,
    ExpectationFailed,
    ImaTeapot,
    MisdirectedRequest,
    UnprocessableEntity,
    Locked,
    FailedDependency,
    UpgradeRequired,
    PreconditionRequired,
    TooManyRequests,
    RequestHeaderFieldsTooLarge,
    UnavailableForLegalReasons,
    InternalServerError,
    NotImplemented,
    BadGateway,
    ServiceUnavailable,
    GatewayTimeout,
    HTTPVersionNotSupported,
    VariantAlsoNegotiates,
    InsufficientStorage,
    LoopDetected,
    NotExtended,
    NetworkAuthenticationRequired,
    Unregistered,
}

impl HttpStatus {
    fn from_code(code: i32) -> HttpStatus {
        match code {
            100 => HttpStatus::Continue,
            101 => HttpStatus::SwitchingProtocols,
            102 => HttpStatus::Processing,
            200 => HttpStatus::OK,
            201 => HttpStatus::Created,
            202 => HttpStatus::Accepted,
            203 => HttpStatus::NonAuthoritativeInformation,
            204 => HttpStatus::NoContent,
            205 => HttpStatus::ResetContent,
            206 => HttpStatus::PartialContent,
            207 => HttpStatus::MultiStatus,
            208 => HttpStatus::AlreadyReported,
            226 => HttpStatus::IMUsed,
            300 => HttpStatus::MultipleChoices,
            301 => HttpStatus::MovedPermanently,
            302 => HttpStatus::Found,
            303 => HttpStatus::SeeOther,
            304 => HttpStatus::NotModified,
            305 => HttpStatus::UseProxy,
            307 => HttpStatus::TemporaryRedirect,
            308 => HttpStatus::PermanentRedirect,
            400 => HttpStatus::BadRequest,
            401 => HttpStatus::Unauthorized,
            402 => HttpStatus::PaymentRequired,
            403 => HttpStatus::Forbidden,
            404 => HttpStatus::NotFound,
            405 => HttpStatus::MethodNotAllowed,
            406 => HttpStatus::NotAcceptable,
            407 => HttpStatus::ProxyAuthenticationRequired,
            408 => HttpStatus::RequestTimeout,
            409 => HttpStatus::Conflict,
            410 => HttpStatus::Gone,
            411 => HttpStatus::LengthRequired,
            412 => HttpStatus::PreconditionFailed,
            413 => HttpStatus::PayloadTooLarge,
            414 => HttpStatus::URITooLong,
            415 => HttpStatus::UnsupportedMediaType,
            416 => HttpStatus::RangeNotSatisfiable,
            417 => HttpStatus::ExpectationFailed,
            418 => HttpStatus::ImaTeapot,
            421 => HttpStatus::MisdirectedRequest,
            422 => HttpStatus::UnprocessableEntity,
            423 => HttpStatus::Locked,
            424 => HttpStatus::FailedDependency,
            426 => HttpStatus::UpgradeRequired,
            428 => HttpStatus::PreconditionRequired,
            429 => HttpStatus::TooManyRequests,
            431 => HttpStatus::RequestHeaderFieldsTooLarge,
            451 => HttpStatus::UnavailableForLegalReasons,
            500 => HttpStatus::InternalServerError,
            501 => HttpStatus::NotImplemented,
            502 => HttpStatus::BadGateway,
            503 => HttpStatus::ServiceUnavailable,
            504 => HttpStatus::GatewayTimeout,
            505 => HttpStatus::HTTPVersionNotSupported,
            506 => HttpStatus::VariantAlsoNegotiates,
            507 => HttpStatus::InsufficientStorage,
            508 => HttpStatus::LoopDetected,
            510 => HttpStatus::NotExtended,
            511 => HttpStatus::NetworkAuthenticationRequired,
            _ => HttpStatus::Unregistered,
        }
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Request {
    id: i32,
    time: Tm,
    url: String,
}

impl Request {
    fn get_matching_response<'a>(&'a self, responses: &'a Vec<Response>) -> Option<&'a Response> {
        for response in responses {
            if self.id == response.id {
                return Some(&response)
            }
        }

        None
    }
}

#[derive(Eq, PartialEq)]
#[derive(Debug)]
pub struct Response {
    id: i32,
    time: Tm,
    mime_type: String,
    response_time: Duration,
    http_status: HttpStatus,
}

pub fn open_logfile(path: &str) -> Result<(Vec<Request>,Vec<Response>), io::Error> {
    let f = try!(File::open(path));

    let f = BufReader::new(f);

    let mut requests: Vec<Request> = Vec::new();
    let mut responses: Vec<Response> = Vec::new();

    for line in f.lines() {
        let line_value = &line.unwrap();

        if line_value.contains("->") {
            let r = try!(parse_request_line(&line_value));
            println!("{:#?}", r);
            requests.push(r)
        }

        if line_value.contains("<-") {
            let r = try!(parse_response_line(&line_value));
            println!("{:#?}", r);
            responses.push(r)
        }

    }

    Ok((requests, responses))
}

pub fn parse_request_line(log_line: &String) -> Result<Request, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let url = parts[5];

    Ok(Request {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        url: url.to_string()
    })
}

pub fn parse_response_line(log_line: &String) -> Result<Response, io::Error> {
    let parts: Vec<&str> = log_line.split(" ").collect();

    let id = parts[2];
    let response_time = parts[parts.len()-1];
    let mime_type = parts[5];
    let status_code = parts[4];

    Ok(Response {
        id: id[1..id.len()-1].parse().unwrap(),
        time: strptime(parts[0], "%d/%b/%Y:%H:%M:%S").unwrap(),
        response_time: Duration::milliseconds(response_time[0..response_time.len()-2].parse().unwrap()),
        mime_type: mime_type.to_string(),
        http_status: HttpStatus::from_code(status_code.parse().unwrap()),
    })
}

pub struct RequestResponsePair<'a> {
    request: &'a Request,
    response: &'a Response
}

pub fn pair_requests_responses<'a>(requests: &'a Vec<Request>, responses: &'a Vec<Response>) -> &'a Vec<RequestResponsePair<'a>> {
    let mut request_response_pairs: Vec<RequestResponsePair> = Vec::new();

    for request in requests {
        match request.get_matching_response(responses) {
            Some(response) => request_response_pairs.push(RequestResponsePair{
                request: request,
                response: response
            }),
            None => println!("none"),
        }
    }

    &request_response_pairs
}

fn main() {
    let lines = open_logfile("src/test/simple-1.log");
    let (requests, responses) = lines.unwrap();
    println!("So many requests: {}", requests.len());
    println!("So many responses: {}", responses.len());
}

#[cfg(test)]
mod tests {
	use super::*;
    extern crate time;
    use time::strptime;
    use::time::Duration;

    #[test]
    fn test_parse_request_line() {
        let line = "08/Apr/2016:09:58:47 +0200 [02] -> GET /content/some/other.html HTTP/1.1".to_string();

        let expected = Request {
            id: 2,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = parse_request_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_parse_response_line() {
        let line = "08/Apr/2016:09:58:48 +0200 [02] <- 200 text/html 10ms".to_string();

        let expected = Response {
            id: 2,
            time: strptime("08/Apr/2016:09:58:48 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(10),
            http_status: HttpStatus::OK,
        };

        let result = parse_response_line(&line);

        assert_eq!(result.unwrap(), expected)
    }

    #[test]
    fn test_open_logfile() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        assert_eq!(requests.len(), 2);
        assert_eq!(responses.len(), 2);
    }

    #[test]
    fn test_get_matching_response() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let result = requests[0].get_matching_response(&responses);

        let expected = Response {
            id: 1,
            time: strptime("08/Apr/2016:09:57:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            mime_type: "text/html".to_string(),
            response_time: Duration::milliseconds(7),
            http_status: HttpStatus::OK,
        };

        assert_eq!(*result.unwrap(), expected);
    }

    #[test]
    fn test_get_matching_response_none_found() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let request_without_matching = Request {
            id: 999,
            time: strptime("08/Apr/2016:09:58:47 +0200", "%d/%b/%Y:%H:%M:%S").unwrap(),
            url: "/content/some/other.html".to_string()
        };

        let result = request_without_matching.get_matching_response(&responses);

        assert!(result.is_none());
    }

    #[test]
    fn test_pair_requests_resonses() {
        let lines = open_logfile("src/test/simple-1.log");
        let (requests, responses) = lines.unwrap();

        let result = pair_requests_responses(&requests, &responses);
    }
}
<|endoftext|>