<commit_before>#[macro_use]
extern crate glium;
extern crate alice;
extern crate rand;

use std::fs::File;
use std::io::Cursor;
use glium::{DisplayBuild, Surface};
use glium::glutin::{Event, ElementState, VirtualKeyCode, MouseScrollDelta};
use alice::model::rendering::{ModelRenderer, prepare_model};
use alice::model::io::read_model;
use alice::model::{Model, Path, Point};
use alice::data::{Vec2, Vec3};
use rand::{thread_rng, Rng};

fn main() {
    let display = glium::glutin::WindowBuilder::new()
        .with_title(format!("Alice"))
        .with_dimensions(1024, 768)
        .with_vsync()
        .build_glium()
        .unwrap();
    let window = display.get_window().unwrap();

    let mut renderer = ModelRenderer::new(&display);

    let model = if let Some(path) = std::env::args().nth(1) {
        let file = File::open(path).unwrap();
        read_model(file).unwrap()
    } else {
        let bytes = include_bytes!("cat.model");
        let file: Cursor<&[u8]> = Cursor::new(bytes);
        read_model(file).unwrap()
    };

    let mut wobble = WobbleModel::new(&model);

    let mut x = 512.0;
    let mut y = 384.0;
    let mut scale = 1.0;

    loop {
        let mut target = display.draw();
        target.clear_color(0.02, 0.02, 0.02, 1.0);

        let (w, h) = window.get_inner_size_points().unwrap();
        renderer.set_size(w as f32, h as f32);

        wobble.tick();
        let model = prepare_model(&display, &wobble.model());
        renderer.draw(&mut target, x, y, scale, &model);

        target.finish().unwrap();

        for ev in display.poll_events() {
            match ev {
                Event::Closed => return,
                Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape)) => return,
                Event::ReceivedCharacter(' ') => wobble.shuffle(),
                Event::ReceivedCharacter('+') => scale *= 1.1,
                Event::ReceivedCharacter('=') => scale *= 1.1,
                Event::ReceivedCharacter('-') => scale /= 1.1,
                Event::ReceivedCharacter('0') => {
                    x = w as f32 / 2.0;
                    y = h as f32 / 2.0;
                    scale = 1.0;
                }
                Event::MouseWheel(MouseScrollDelta::PixelDelta(dx, dy)) => {
                    x -= dx;
                    y += dy;
                },
                _ => ()
            }
        }
    }
}

struct WobbleModel {
    paths: Vec<WobblePath>
}

struct WobblePath {
    colour: Vec3,
    points: Vec<WobblePoint>
}

struct WobblePoint {
    location: Vec2,
    curve_bias: f64,
    offset: Vec2,
    velocity: Vec2
}

const N: f64 = 30.0;
const K: f64 = 0.8;
const D: f64 = 0.9;

impl WobbleModel {
    pub fn new(model: &Model) -> WobbleModel {
        WobbleModel {
            paths: model.paths
                .iter()
                .map(|path| WobblePath {
                    colour: path.colour,
                    points: path.points
                        .iter()
                        .map(|point| WobblePoint {
                            location: point.location,
                            curve_bias: point.curve_bias,
                            offset: (0.0, 0.0),
                            velocity: (0.0, 0.0)
                        })
                        .collect()
                })
                .collect()
        }
    }

    pub fn model(&self) -> Model {
        Model {
            paths: self.paths
                .iter()
                .map(|path| Path {
                    colour: path.colour,
                    points: path.points
                        .iter()
                        .map(|point| {
                            let x = point.location.0 + point.offset.0;
                            let y = point.location.1 + point.offset.1;
                            Point {
                                location: (x, y),
                                curve_bias: point.curve_bias
                            }
                        })
                        .collect()
                })
                .collect()
        }
    }

    pub fn shuffle(&mut self) {
        let mut rng = rand::thread_rng();
        for path in self.paths.iter_mut() {
            for point in path.points.iter_mut() {
                point.offset.0 += rng.gen_range(-N, N);
                point.offset.1 += rng.gen_range(-N, N);
            }
        }
    }

    pub fn tick(&mut self) {
        for path in self.paths.iter_mut() {
            for point in path.points.iter_mut() {
                let (mut ox, mut oy) = point.offset;
                let (mut vx, mut vy) = point.velocity;

                vx += -K * ox;
                vy += -K * oy;
                vx *= D;
                vy *= D;

                ox += vx;
                oy += vy;

                point.offset = (ox, oy);
                point.velocity = (vx, vy);
            }
        }
    }
}
<commit_msg>Make wobbling more interactive<commit_after>#[macro_use]
extern crate glium;
extern crate alice;
extern crate rand;

use std::fs::File;
use std::io::Cursor;
use glium::{DisplayBuild, Surface};
use glium::glutin::{Event, ElementState, VirtualKeyCode, MouseScrollDelta, MouseButton};
use alice::model::rendering::{ModelRenderer, prepare_model};
use alice::model::io::read_model;
use alice::model::{Model, Path, Point};
use alice::data::{Vec2, Vec3};
use rand::{thread_rng, Rng};

fn main() {
    let display = glium::glutin::WindowBuilder::new()
        .with_title(format!("Alice"))
        .with_dimensions(1024, 768)
        .with_vsync()
        .build_glium()
        .unwrap();
    let window = display.get_window().unwrap();

    let mut renderer = ModelRenderer::new(&display);

    let model = if let Some(path) = std::env::args().nth(1) {
        let file = File::open(path).unwrap();
        read_model(file).unwrap()
    } else {
        let bytes = include_bytes!("cat.model");
        let file: Cursor<&[u8]> = Cursor::new(bytes);
        read_model(file).unwrap()
    };

    let mut wobble = WobbleModel::new(&model, 0.5, 0.95);

    let mut x = 512.0;
    let mut y = 384.0;
    let mut scale = 1.0;
    let mut mouse_pos = (0, 0);

    loop {
        let mut target = display.draw();
        target.clear_color(0.02, 0.02, 0.02, 1.0);

        let (w, h) = window.get_inner_size_points().unwrap();
        renderer.set_size(w as f32, h as f32);

        wobble.tick();
        let model = prepare_model(&display, &wobble.model());
        renderer.draw(&mut target, x, y, scale, &model);

        target.finish().unwrap();

        for ev in display.poll_events() {
            match ev {
                Event::Closed => return,
                Event::KeyboardInput(ElementState::Pressed, _, Some(VirtualKeyCode::Escape)) => return,
                Event::ReceivedCharacter(' ') => wobble.shuffle(10.0 / scale as f64),
                Event::ReceivedCharacter('+') => scale *= 1.1,
                Event::ReceivedCharacter('=') => scale *= 1.1,
                Event::ReceivedCharacter('-') => scale /= 1.1,
                Event::ReceivedCharacter('0') => {
                    x = w as f32 / 2.0;
                    y = h as f32 / 2.0;
                    scale = 1.0;
                }
                Event::MouseWheel(MouseScrollDelta::PixelDelta(dx, dy)) => {
                    x -= dx;
                    y += dy;
                },
                Event::MouseMoved(p) => mouse_pos = p,
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    let f = window.hidpi_factor() as f64;
                    let (mx, my) = (mouse_pos.0 as f64 / f, h as f64 - mouse_pos.1 as f64 / f);
                    let (mx, my) = (mx - x as f64, my - y as f64);
                    let (mx, my) = (mx / scale as f64, my / scale as f64);
                    wobble.push((mx, my), 0.3, 100.0 / scale as f64)
                },
                _ => ()
            }
        }
    }
}

struct WobbleModel {
    spring: f64,
    damping: f64,
    paths: Vec<WobblePath>,
}

struct WobblePath {
    colour: Vec3,
    points: Vec<WobblePoint>
}

struct WobblePoint {
    location: Vec2,
    curve_bias: f64,
    offset: Vec2,
    velocity: Vec2
}

fn gauss(c: f64, x: f64) -> f64 {
    (-x * x / (2.0 * c * c)).exp()
}

impl WobbleModel {
    pub fn new(model: &Model, spring: f64, damping: f64) -> WobbleModel {
        WobbleModel {
            spring: spring,
            damping: damping,
            paths: model.paths
                .iter()
                .map(|path| WobblePath {
                    colour: path.colour,
                    points: path.points
                        .iter()
                        .map(|point| WobblePoint {
                            location: point.location,
                            curve_bias: point.curve_bias,
                            offset: (0.0, 0.0),
                            velocity: (0.0, 0.0)
                        })
                        .collect()
                })
                .collect()
        }
    }

    pub fn model(&self) -> Model {
        Model {
            paths: self.paths
                .iter()
                .map(|path| Path {
                    colour: path.colour,
                    points: path.points
                        .iter()
                        .map(|point| {
                            let x = point.location.0 + point.offset.0;
                            let y = point.location.1 + point.offset.1;
                            Point {
                                location: (x, y),
                                curve_bias: point.curve_bias
                            }
                        })
                        .collect()
                })
                .collect()
        }
    }

    pub fn shuffle(&mut self, v: f64) {
        let mut rng = rand::thread_rng();
        for path in self.paths.iter_mut() {
            for point in path.points.iter_mut() {
                point.velocity.0 += rng.gen_range(-v, v);
                point.velocity.1 += rng.gen_range(-v, v);
            }
        }
    }

    pub fn push(&mut self, (x, y): Vec2, s: f64, w: f64) {
        for path in self.paths.iter_mut() {
            for point in path.points.iter_mut() {
                let (px, py) = point.location;
                let (dx, dy) = (px - x, py - y);
                let d = (dx * dx + dy * dy).sqrt();
                let d = s * gauss(w, d);

                point.velocity.0 += d * dx;
                point.velocity.1 += d * dy;
            }
        }
    }

    pub fn tick(&mut self) {
        for path in self.paths.iter_mut() {
            for point in path.points.iter_mut() {
                let (mut ox, mut oy) = point.offset;
                let (mut vx, mut vy) = point.velocity;

                vx += -self.spring * ox;
                vy += -self.spring * oy;
                vx *= self.damping;
                vy *= self.damping;

                ox += vx;
                oy += vy;

                point.offset = (ox, oy);
                point.velocity = (vx, vy);
            }
        }
    }
}
<|endoftext|>


<commit_before>extern crate csv;

use std::io::{File};
use std::num::{abs};
use std::os;

static CACHE_SIZE: uint = 16384;

fn main() {
    // Extract input file
    let args: Vec<String> = os::args();
    if args.len() != 2 {
      fail!("No input file provided. Please pass one as argument!");
    }

    // Open CSV file with statistics
    let file = File::open(&Path::new(args[1].as_slice()));
    let mut rdr = csv::Decoder::from_reader(file);
    rdr.separator(';');
    rdr.has_headers(true);
    rdr.enforce_same_length(false);
    // Ignore comment lines generated by benchmark suite
    for i in range(0u,3) {
      let ignored = rdr.iter().next();
      // println!("{} length", ignored.unwrap().len());
    }

    // Vectors for storage of data items
    let mut data : Vec<Vec<int>> = Vec::with_capacity(3);
    data.push(Vec::with_capacity(CACHE_SIZE)); // event latency
    data.push(Vec::with_capacity(CACHE_SIZE)); // dispatch latency + event latency
    data.push(Vec::with_capacity(CACHE_SIZE)); // execution time

    for (event_latency, dispatch_latency, exec_time) in rdr.decode_iter::<(int, int, int)>() {
        // println!("{}, {}, {}", event_latency, dispatch_latency, exec_time);
        data.get_mut(0).push(event_latency);
        data.get_mut(1).push(dispatch_latency);
        data.get_mut(2).push(exec_time);
    }

    // Sort vectors
    data.get_mut(0).sort_by(|a, b| compare_ints(a, b)); // |a, b| a.cmp(b)
    data.get_mut(1).sort_by(|a, b| compare_ints(a, b));
    data.get_mut(2).sort_by(|a, b| compare_ints(a, b));

    // for item in data[0].iter() {
    //     println!("{}", item);
    // }
    for dataset_id in range(0u, 3) {
        let dataset = &data[dataset_id];
        println!("Dataset {} with {} entries:", dataset_id, dataset.len());
        // Statistics about current dataset
        let (quantile_min, quantile_max) = get_quantiles(dataset);
        println!("First & Last & 5% & 95% & average & stdDev & avgMedianDev")
        println!("{} & {} & {} & {} & {} & {} & {}",
                 get_first(dataset), get_last(dataset),
                 quantile_min, quantile_max,
                 get_avg(dataset), get_variance(dataset).sqrt(),
                 get_average_abs_deviation(dataset));

        println!("-----------------------------")
    }
}

fn get_first(input: &Vec<int>) -> int {
    input[0]
}

fn get_last(input: &Vec<int>) -> int {
    input[input.len() - 1]
}

fn get_avg(input: &Vec<int>) -> f32 {
    let mut sum = 0f32;

    for item in input.iter() {
        sum += *item as f32;
    }
    // Result
    return sum / (input.len() as f32);
}

fn get_median(input: &Vec<int>) -> int {
    let midway = input.len() / 2;
    // Result
    input[midway]
}

fn get_variance(input: &Vec<int>) -> f32 {
    let avg: f32 = get_avg(input);
    let mut variance = 0f32;

    for item in input.iter() {
        let diff = (*item as f32) - avg;
        variance += diff * diff;
    }

    variance /= (input.len() - 1) as f32;
    // Result
    variance
}

/// Average absolute deviation about median
///
/// Implement http://en.wikipedia.org/wiki/Median_absolute_deviation instead
fn get_average_abs_deviation(input: &Vec<int>) -> f32 {
    let median = get_median(input);

    let mut sum = 0i;
    for item in input.iter() {
        let diff = abs(item - median);
        sum += diff;
    }
    let res = (sum as f32) / (input.len() as f32);
    // Result
    res
}

/// Calculate 5% and 95% quantiles
fn get_quantiles(input : &Vec<int>) -> (int, int) {
    let low_index = ((input.len()-1) * 5u) / 100u;
    let high_index = ((input.len()-1) * 95u) / 100u;
    // Result
    (input[low_index], input[high_index])
}

fn compare_ints(left : &int, right : &int) -> Ordering {
    if left < right {
      Less
    } else if left > right {
      Greater
    } else {
      Equal
    }
}
<commit_msg>Added Median deviation about the mean (MAD)<commit_after>extern crate csv;

use std::io::{File};
use std::num::{abs};
use std::os;

static CACHE_SIZE: uint = 16384;

fn main() {
    // Extract input file
    let args: Vec<String> = os::args();
    if args.len() != 2 {
      fail!("No input file provided. Please pass one as argument!");
    }

    // Open CSV file with statistics
    let file = File::open(&Path::new(args[1].as_slice()));
    let mut rdr = csv::Decoder::from_reader(file);
    rdr.separator(';');
    rdr.has_headers(true);
    rdr.enforce_same_length(false);
    // Ignore comment lines generated by benchmark suite
    for i in range(0u,3) {
      let ignored = rdr.iter().next();
      // println!("{} length", ignored.unwrap().len());
    }

    // Vectors for storage of data items
    let mut data : Vec<Vec<int>> = Vec::with_capacity(3);
    data.push(Vec::with_capacity(CACHE_SIZE)); // event latency
    data.push(Vec::with_capacity(CACHE_SIZE)); // dispatch latency + event latency
    data.push(Vec::with_capacity(CACHE_SIZE)); // execution time

    for (event_latency, dispatch_latency, exec_time) in rdr.decode_iter::<(int, int, int)>() {
        // println!("{}, {}, {}", event_latency, dispatch_latency, exec_time);
        data.get_mut(0).push(event_latency);
        data.get_mut(1).push(dispatch_latency);
        data.get_mut(2).push(exec_time);
    }

    // Sort vectors
    data.get_mut(0).sort_by(|a, b| compare_ints(a, b)); // |a, b| a.cmp(b)
    data.get_mut(1).sort_by(|a, b| compare_ints(a, b));
    data.get_mut(2).sort_by(|a, b| compare_ints(a, b));

    // for item in data[0].iter() {
    //     println!("{}", item);
    // }
    for dataset_id in range(0u, 3) {
        let dataset = &data[dataset_id];
        println!("Dataset {} with {} entries:", dataset_id, dataset.len());
        // Statistics about current dataset
        let (quantile_min, quantile_max) = get_quantiles(dataset);
        println!("First & Last & 5% & 95% & average & stdDev & avgMedianDev & medianDev")
        println!("{} & {} & {} & {} & {} & {} & {} & {}",
                 get_first(dataset), get_last(dataset),
                 quantile_min, quantile_max,
                 get_avg(dataset), get_variance(dataset).sqrt(),
                 get_average_abs_deviation(dataset),
                 get_median_abs_deviation(dataset));

        println!("-----------------------------")
    }
}

fn get_first(input: &Vec<int>) -> int {
    input[0]
}

fn get_last(input: &Vec<int>) -> int {
    input[input.len() - 1]
}

fn get_avg(input: &Vec<int>) -> f32 {
    let mut sum = 0f32;

    for item in input.iter() {
        sum += *item as f32;
    }
    // Result
    return sum / (input.len() as f32);
}

fn get_median(input: &Vec<int>) -> int {
    let midway = input.len() / 2;
    // Result
    input[midway]
}

fn get_variance(input: &Vec<int>) -> f32 {
    let avg: f32 = get_avg(input);
    let mut variance = 0f32;

    for item in input.iter() {
        let diff = (*item as f32) - avg;
        variance += diff * diff;
    }

    variance /= (input.len() - 1) as f32;
    // Result
    variance
}

/// Average absolute deviation about median
///
/// Implement http://en.wikipedia.org/wiki/Median_absolute_deviation instead
fn get_average_abs_deviation(input: &Vec<int>) -> f32 {
    let median = get_median(input);

    let mut sum = 0i;
    for item in input.iter() {
        let diff = abs(item - median);
        sum += diff;
    }
    let res = (sum as f32) / (input.len() as f32);
    // Result
    res
}

/// Median absolute deviation (MAD)
fn get_median_abs_deviation(input: &Vec<int>) -> int {
    let median = get_median(input);

    let mut abs_deviations: Vec<int> = Vec::with_capacity(input.len());
    for item in input.iter().map(|&x| abs(x - median)) {
        abs_deviations.push(item);
    }
    // Sort new deviations to calculate the median
    abs_deviations.sort_by(|a, b| compare_ints(a, b));
    // Return median
    get_median(&abs_deviations)
}

/// Calculate 5% and 95% quantiles
fn get_quantiles(input : &Vec<int>) -> (int, int) {
    let low_index = ((input.len()-1) * 5u) / 100u;
    let high_index = ((input.len()-1) * 95u) / 100u;
    // Result
    (input[low_index], input[high_index])
}

fn compare_ints(left : &int, right : &int) -> Ordering {
    if left < right {
      Less
    } else if left > right {
      Greater
    } else {
      Equal
    }
}
<|endoftext|>


<commit_before>//! An implementation of [tldr](https://github.com/tldr-pages/tldr) in Rust.

#[macro_use] extern crate log;
#[cfg(feature = "logging")]extern crate env_logger;
extern crate docopt;
extern crate ansi_term;
extern crate flate2;
extern crate tar;
extern crate tempdir;
extern crate curl;
extern crate rustc_serialize;

use std::io::BufReader;
use std::fs::File;
use std::process;

use docopt::Docopt;

mod types;
mod tokenizer;
mod formatter;
mod updater;
mod error;

use tokenizer::Tokenizer;
use updater::Updater;
use error::TldrError;
use formatter::print_lines;

const NAME: &'static str = "tldr-rs";
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
Usage:

    tldr <command>
    tldr [options]

Options:

    -h --help           Show this screen
    -v --version        Show version information
    -l --list           List all commands in the cache
    -f --render <file>  Render a specific markdown file
    -o --os <type>      Override the operating system [linux, osx, sunos]
    -u --update         Update the local cache
    -c --clear-cache    Clear the local cache

Examples:

    $ tldr tar
    $ tldr --list

To control the cache:

    $ tldr --update
    $ tldr --clear-cache

To render a local file (for testing):

    $ tldr --render /path/to/file.md
";
const ARCHIVE_URL: &'static str = "https://github.com/tldr-pages/tldr/archive/master.tar.gz";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_command: Option<String>,
    flag_help: bool,
    flag_version: bool,
    flag_list: bool,
    flag_render: Option<String>,
    flag_os: Option<String>,  // TODO enum
    flag_update: bool,
    flag_clear_cache: bool,
}

/// Open file, return a `BufRead` instance
fn get_file_reader(filepath: &str) -> Result<BufReader<File>, String> {
    let file = try!(
        File::open(filepath)
            .map_err(|msg| format!("Could not open file: {}", msg))
    );
    Ok(BufReader::new(file))
}

#[cfg(feature = "logging")]
fn init_log() {
    env_logger::init().unwrap();
}

#[cfg(not(feature = "logging"))]
fn init_log() { }

fn main() {

    // Initialize logger
    init_log();

    // Parse arguments
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());
    println!("{:#?}", args);

    // Show version and exit
    if args.flag_version {
        println!("{} v{}", NAME, VERSION);
        process::exit(0);
    }

    // Clear cache, pass through
    if args.flag_clear_cache {
        println!("Flag --clear-cache not yet implemented.");
        process::exit(1);
    }

    // Update cache, pass through
    if args.flag_update {
        let dl = Updater::new(ARCHIVE_URL);
        let copied = dl.update().unwrap_or_else(|e| {
            match e {
                TldrError::UpdateError(msg) => println!("Could not update cache: {}", msg),
            };
            process::exit(1);
        });
        println!("Cached {} tldr pages.", copied);
    }

    // Render local file and exit
    if let Some(file) = args.flag_render {
        // Open file
        let reader = get_file_reader(&file).unwrap_or_else(|msg| {
            println!("{}", msg);
            process::exit(1);
        });

        // Create tokenizer and print output
        let mut tokenizer = Tokenizer::new(reader);
        print_lines(&mut tokenizer);

        process::exit(0);
    }

    // List cached commands and exit
    if args.flag_list {
        println!("Flag --list not yet implemented.");
        process::exit(1);
    }

    // Override OS and exit
    if let Some(os) = args.flag_os {
        println!("Flag --os not yet implemented.");
    }

    let dl = Updater::new("https://github.com/tldr-pages/tldr/archive/master.tar.gz".into());
    let copied = dl.update().unwrap_or_else(|e| {
        match e {
            TldrError::UpdateError(msg) => println!("Could not update cache: {}", msg),
        };
        process::exit(1);
    });
    println!("Cached {} tldr pages.", copied);

}

#[cfg(test)]
mod test {
    use types::LineType;

    #[test]
    fn test_linetype_from_str() {
        assert_eq!(LineType::from(""), LineType::Empty);
        assert_eq!(LineType::from(" \n \r"), LineType::Empty);
        assert_eq!(LineType::from("# Hello there"), LineType::Title("Hello there".into()));
        assert_eq!(LineType::from("> tis a description \n"), LineType::Description("tis a description".into()));
        assert_eq!(LineType::from("- some command"), LineType::ExampleText("some command".into()));
        assert_eq!(LineType::from("`$ cargo run`"), LineType::ExampleCode("$ cargo run".into()));
        assert_eq!(LineType::from("`$ cargo run"), LineType::Other("`$ cargo run".into()));
        assert_eq!(LineType::from("jklö"), LineType::Other("jklö".into()));
    }
}
<commit_msg>Implement command handling<commit_after>//! An implementation of [tldr](https://github.com/tldr-pages/tldr) in Rust.

#[macro_use] extern crate log;
#[cfg(feature = "logging")]extern crate env_logger;
extern crate docopt;
extern crate ansi_term;
extern crate flate2;
extern crate tar;
extern crate tempdir;
extern crate curl;
extern crate rustc_serialize;

use std::io::BufReader;
use std::fs::File;
use std::process;

use docopt::Docopt;

mod types;
mod tokenizer;
mod formatter;
mod updater;
mod error;

use tokenizer::Tokenizer;
use updater::Updater;
use error::TldrError;
use formatter::print_lines;

const NAME: &'static str = "tldr-rs";
const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
Usage:

    tldr <command>
    tldr [options]

Options:

    -h --help           Show this screen
    -v --version        Show version information
    -l --list           List all commands in the cache
    -f --render <file>  Render a specific markdown file
    -o --os <type>      Override the operating system [linux, osx, sunos]
    -u --update         Update the local cache
    -c --clear-cache    Clear the local cache

Examples:

    $ tldr tar
    $ tldr --list

To control the cache:

    $ tldr --update
    $ tldr --clear-cache

To render a local file (for testing):

    $ tldr --render /path/to/file.md
";
const ARCHIVE_URL: &'static str = "https://github.com/tldr-pages/tldr/archive/master.tar.gz";

#[derive(Debug, RustcDecodable)]
struct Args {
    arg_command: Option<String>,
    flag_help: bool,
    flag_version: bool,
    flag_list: bool,
    flag_render: Option<String>,
    flag_os: Option<String>,  // TODO enum
    flag_update: bool,
    flag_clear_cache: bool,
}

/// Open file, return a `BufRead` instance
fn get_file_reader(filepath: &str) -> Result<BufReader<File>, String> {
    let file = try!(
        File::open(filepath)
            .map_err(|msg| format!("Could not open file: {}", msg))
    );
    Ok(BufReader::new(file))
}

#[cfg(feature = "logging")]
fn init_log() {
    env_logger::init().unwrap();
}

#[cfg(not(feature = "logging"))]
fn init_log() { }

fn main() {

    // Initialize logger
    init_log();

    // Parse arguments
    let args: Args = Docopt::new(USAGE)
                            .and_then(|d| d.decode())
                            .unwrap_or_else(|e| e.exit());
    println!("{:#?}", args);

    // Show version and exit
    if args.flag_version {
        println!("{} v{}", NAME, VERSION);
        process::exit(0);
    }

    // Clear cache, pass through
    if args.flag_clear_cache {
        println!("Flag --clear-cache not yet implemented.");
        process::exit(1);
    }

    // Update cache, pass through
    if args.flag_update {
        let dl = Updater::new(ARCHIVE_URL);
        let copied = dl.update().unwrap_or_else(|e| {
            match e {
                TldrError::UpdateError(msg) => println!("Could not update cache: {}", msg),
            };
            process::exit(1);
        });
        println!("Cached {} tldr pages.", copied);
    }

    // Render local file and exit
    if let Some(file) = args.flag_render {
        // Open file
        let reader = get_file_reader(&file).unwrap_or_else(|msg| {
            println!("{}", msg);
            process::exit(1);
        });

        // Create tokenizer and print output
        let mut tokenizer = Tokenizer::new(reader);
        print_lines(&mut tokenizer);

        process::exit(0);
    }

    // List cached commands and exit
    if args.flag_list {
        println!("Flag --list not yet implemented.");
        process::exit(1);
    }

    // Override OS and exit
    if let Some(os) = args.flag_os {
        println!("Flag --os not yet implemented.");
    }

    // Show command from cache
    if let Some(command) = args.arg_command {
        println!("Flag --os not yet implemented.");
        process::exit(1);
    }

    println!("{}", USAGE);
    process::exit(1);
}

#[cfg(test)]
mod test {
    use types::LineType;

    #[test]
    fn test_linetype_from_str() {
        assert_eq!(LineType::from(""), LineType::Empty);
        assert_eq!(LineType::from(" \n \r"), LineType::Empty);
        assert_eq!(LineType::from("# Hello there"), LineType::Title("Hello there".into()));
        assert_eq!(LineType::from("> tis a description \n"), LineType::Description("tis a description".into()));
        assert_eq!(LineType::from("- some command"), LineType::ExampleText("some command".into()));
        assert_eq!(LineType::from("`$ cargo run`"), LineType::ExampleCode("$ cargo run".into()));
        assert_eq!(LineType::from("`$ cargo run"), LineType::Other("`$ cargo run".into()));
        assert_eq!(LineType::from("jklö"), LineType::Other("jklö".into()));
    }
}
<|endoftext|>


<commit_before>extern crate bytes;
extern crate env_logger;
extern crate mio;
#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
extern crate failure;
extern crate slab;

use clap::{App, Arg};
use failure::Error;
use failure::ResultExt;
use mio::net::{TcpListener, TcpStream};
use slab::Slab;
use std::collections::VecDeque;
use std::io::{Read, Write};
use std::ops;

#[derive(Debug)]
enum MiChatCommand {
    Broadcast(String),
    NewConnection(TcpStream),
}

struct MiChat {
    connections: Slab<EventHandler>,
}

trait RuleHandler {
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready);
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error>;
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error>;
    fn is_closed(&self) -> bool;
}

impl MiChat {
    fn new() -> MiChat {
        MiChat {
            connections: Slab::new(),
        }
    }

    fn listen(&mut self, event_loop: &mut mio::Poll, listener: TcpListener) -> Result<(), Error> {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Listener(Listener::new(listener, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token)?;
        Ok(())
    }

    fn process_action(&mut self, msg: MiChatCommand, event_loop: &mut mio::Poll) {
        trace!("{:p}; got {:?}", self, msg);
        match msg {
            MiChatCommand::Broadcast(s) => self.process_broadcast(s),
            MiChatCommand::NewConnection(socket) => self.process_new_connection(event_loop, socket),
        }
    }

    fn process_broadcast(&mut self, s: String) {
        for c in self.client_connections_mut() {
            c.enqueue(&s);
        }
    }

    fn process_new_connection(&mut self, event_loop: &mut mio::Poll, socket: mio::tcp::TcpStream) {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Conn(Connection::new(socket, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token);
    }

    fn client_connections_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut Connection> + 'a {
        self.connections.iter_mut().filter_map(|(_, it)| match it {
            &mut EventHandler::Conn(ref mut c) => Some(c),
            _ => None,
        })
    }
}

#[derive(Debug)]
struct Connection {
    socket: TcpStream,
    sock_status: mio::Ready,
    token: mio::Token,
    read_buf: Vec<u8>,
    read_eof: bool,
    failed: bool,
    write_buf: Vec<u8>,
}

impl RuleHandler for Connection {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.socket,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("event loop register")?;
            Ok(())
    }

    // Event updates arrive here
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        self.sock_status.insert(events);
        info!(
            "Connection::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.socket.peer_addr(),
            events,
            self.sock_status
        );
    }

    // actions are processed here on down.

    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            self.sock_status.remove(mio::Ready::readable());
            self.read()?;
        }

        self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.sock_status.remove(mio::Ready::writable());
            self.write()?;
        }

        if !self.is_closed() {
            self.reregister(event_loop)?;
        }

        Ok(())
    }

    fn is_closed(&self) -> bool {
        self.failed || (self.read_eof && self.write_buf.is_empty())
    }
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token) -> Connection {
        Connection {
            socket: socket,
            sock_status: mio::Ready::empty(),
            token: token,
            read_buf: Vec::with_capacity(1024),
            write_buf: Vec::new(),
            read_eof: false,
            failed: false,
        }
    }
    fn process_buffer(&mut self, to_parent: &mut FnMut(MiChatCommand)) {
        let mut prev = 0;
        info!(
            "{:?}: Read buffer: {:?}",
            self.socket.peer_addr(),
            self.read_buf
        );
        for n in self.read_buf.iter().enumerate().filter_map(|(i, e)| {
            if *e == '\n' as u8 {
                Some(i)
            } else {
                None
            }
        }) {
            info!(
                "{:?}: Pos: {:?}; chunk: {:?}",
                self.socket.peer_addr(),
                n,
                &self.read_buf[prev..n]
            );
            let s = String::from_utf8_lossy(&self.read_buf[prev..n]).to_string();
            let cmd = MiChatCommand::Broadcast(s);
            info!("Send! {:?}", cmd);
            to_parent(cmd);
            prev = n + 1;
        }
        let remainder = self.read_buf[prev..].to_vec();
        info!(
            "{:?}: read Remainder: {}",
            self.socket.peer_addr(),
            remainder.len()
        );
        self.read_buf = remainder;
    }

    fn read(&mut self) -> Result<(), Error> {
        let mut abuf = vec![0; 1024];
        match self.socket.read(&mut abuf).context("Reading socket")? {
            0 => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true;
                Ok(())
            }
            n => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.read_buf.extend(&abuf[0..n]);
                Ok(())
            }
        }
    }

    fn write(&mut self) -> Result<(), Error> {
        let n = self
            .socket
            .write(&mut self.write_buf)
            .context("writing buffer")?;
        info!(
            "{:?}: Wrote {} of {} in buffer",
            self.socket.peer_addr(),
            n,
            self.write_buf.len()
        );
        self.write_buf = self.write_buf[n..].to_vec();
        info!(
            "{:?}: Now {:?}b",
            self.socket.peer_addr(),
            self.write_buf.len()
        );
        Ok(())
    }

    fn reregister(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut flags = mio::Ready::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::Ready::writable());
        }
        info!("Registering {:?} with {:?}", self, flags);

        event_loop
            .reregister(&self.socket, self.token, flags, mio::PollOpt::oneshot())
            .context("EventLoop#reregister")?;

        Ok(())
    }

    fn enqueue(&mut self, s: &str) {
        self.write_buf.extend(s.as_bytes());
        self.write_buf.push('\n' as u8);
    }
}

#[derive(Debug)]
struct Listener {
    listener: TcpListener,
    sock_status: mio::Ready,
    token: mio::Token,
}

impl Listener {
    fn new(listener: TcpListener, token: mio::Token) -> Listener {
        Listener {
            listener: listener,
            sock_status: mio::Ready::empty(),
            token: token,
        }
    }
    fn reregister(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .reregister(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
            Ok(())
    }
}

impl RuleHandler for Listener {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
            Ok(())
    }

    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        assert!(events.is_readable());
        self.sock_status.insert(events);
        info!(
            "Listener::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.listener.local_addr(),
            events,
            self.sock_status
        );
    }
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            info!("the listener socket is ready to accept a connection");
            self.sock_status.remove(mio::Ready::readable());
            match self.listener.accept().context("Accept listening socket")? {
                (socket, _client) => {
                    let cmd = MiChatCommand::NewConnection(socket);
                    to_parent(cmd);
                }
            }

            self.reregister(event_loop, self.token)?;
        }
        Ok(())
    }

    fn is_closed(&self) -> bool {
        false
    }
}

#[derive(Debug)]
enum EventHandler {
    Listener(Listener),
    Conn(Connection),
}

impl ops::Deref for EventHandler {
    type Target = RuleHandler;
    fn deref(&self) -> &Self::Target {
        match self {
            &EventHandler::Conn(ref conn) => conn,
            &EventHandler::Listener(ref listener) => listener,
        }
    }
}
impl ops::DerefMut for EventHandler {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            &mut EventHandler::Conn(ref mut conn) => conn,
            &mut EventHandler::Listener(ref mut listener) => listener,
        }
    }
}

impl MiChat {
    fn ready(&mut self, event_loop: &mut mio::Poll, token: mio::Token, events: mio::Ready) {
        info!("{:?}: {:?}", token, events);
        self.connections[token.into()].handle_event(event_loop, events);
        if self.connections[token.into()].is_closed() {
            info!("Removing; {:?}", token);
            self.connections.remove(token.into());
        }

        let mut parent_actions = VecDeque::new();
        let mut failed = Vec::new();
        loop {
            for (idx, conn) in self.connections.iter_mut() {
                if let Err(e) =
                    conn.process_rules(event_loop, &mut |action| parent_actions.push_back(action))
                {
                    failed.push(idx);
                    error!("Error on connection {:?}; Dropping: {:?}", idx, e);
                }
            }
            for idx in failed.drain(..) {
                self.connections.remove(idx);
            }
            // Anything left to process?
            if parent_actions.is_empty() {
                break;
            }

            for action in parent_actions.drain(..) {
                self.process_action(action, event_loop);
            }
        }
    }

    fn run(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        loop {
            self.run_once(event_loop)?;
        }
    }
    fn run_once(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut events = mio::Events::with_capacity(1024);
        event_loop.poll(&mut events, None)?;
        for ev in &events {
            self.ready(event_loop, ev.token(), ev.readiness())
        }

        Ok(())
    }
}

fn main() {
    env_logger::init();
    let matches = App::new("michat")
        .arg(
            Arg::with_name("bind")
                .short("l")
                .takes_value(true)
                .required(true),
        ).get_matches();

    let address = value_t_or_exit!(matches.value_of("bind"), std::net::SocketAddr);
    let listener = TcpListener::bind(&address).expect("bind");

    let mut event_loop = mio::Poll::new().expect("Create event loop");
    let mut service = MiChat::new();

    service.listen(&mut event_loop, listener).expect("Starting listener");
    info!("running michat listener at: {:?}", address);
    service.run(&mut event_loop).expect("run");
}
<commit_msg>Split event observe / process.<commit_after>extern crate bytes;
extern crate env_logger;
extern crate mio;
#[macro_use]
extern crate log;
#[macro_use]
extern crate clap;
extern crate failure;
extern crate slab;

use clap::{App, Arg};
use failure::Error;
use failure::ResultExt;
use mio::net::{TcpListener, TcpStream};
use slab::Slab;
use std::collections::VecDeque;
use std::io::{Read, Write};
use std::ops;

#[derive(Debug)]
enum MiChatCommand {
    Broadcast(String),
    NewConnection(TcpStream),
}

struct MiChat {
    connections: Slab<EventHandler>,
}

trait RuleHandler {
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready);
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error>;
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error>;
    fn is_closed(&self) -> bool;
}

impl MiChat {
    fn new() -> MiChat {
        MiChat {
            connections: Slab::new(),
        }
    }

    fn listen(&mut self, event_loop: &mut mio::Poll, listener: TcpListener) -> Result<(), Error> {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Listener(Listener::new(listener, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token)?;
        Ok(())
    }

    fn process_action(&mut self, msg: MiChatCommand, event_loop: &mut mio::Poll) {
        trace!("{:p}; got {:?}", self, msg);
        match msg {
            MiChatCommand::Broadcast(s) => self.process_broadcast(s),
            MiChatCommand::NewConnection(socket) => self.process_new_connection(event_loop, socket),
        }
    }

    fn process_broadcast(&mut self, s: String) {
        for c in self.client_connections_mut() {
            c.enqueue(&s);
        }
    }

    fn process_new_connection(&mut self, event_loop: &mut mio::Poll, socket: mio::tcp::TcpStream) {
        let token = {
            let e = self.connections.vacant_entry();
            let token = mio::Token(e.key());
            let l = EventHandler::Conn(Connection::new(socket, token));
            e.insert(l);
            token
        };
        &self.connections[token.into()].register(event_loop, token);
    }

    fn client_connections_mut<'a>(&'a mut self) -> impl Iterator<Item = &'a mut Connection> + 'a {
        self.connections.iter_mut().filter_map(|(_, it)| match it {
            &mut EventHandler::Conn(ref mut c) => Some(c),
            _ => None,
        })
    }
}

#[derive(Debug)]
struct Connection {
    socket: TcpStream,
    sock_status: mio::Ready,
    token: mio::Token,
    read_buf: Vec<u8>,
    read_eof: bool,
    failed: bool,
    write_buf: Vec<u8>,
}

impl RuleHandler for Connection {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.socket,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("event loop register")?;
            Ok(())
    }

    // Event updates arrive here
    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        self.sock_status.insert(events);
        info!(
            "Connection::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.socket.peer_addr(),
            events,
            self.sock_status
        );
    }

    // actions are processed here on down.

    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            self.sock_status.remove(mio::Ready::readable());
            self.read()?;
        }

        self.process_buffer(to_parent);

        if self.sock_status.is_writable() {
            self.sock_status.remove(mio::Ready::writable());
            self.write()?;
        }

        if !self.is_closed() {
            self.reregister(event_loop)?;
        }

        Ok(())
    }

    fn is_closed(&self) -> bool {
        self.failed || (self.read_eof && self.write_buf.is_empty())
    }
}

impl Connection {
    fn new(socket: TcpStream, token: mio::Token) -> Connection {
        Connection {
            socket: socket,
            sock_status: mio::Ready::empty(),
            token: token,
            read_buf: Vec::with_capacity(1024),
            write_buf: Vec::new(),
            read_eof: false,
            failed: false,
        }
    }
    fn process_buffer(&mut self, to_parent: &mut FnMut(MiChatCommand)) {
        let mut prev = 0;
        info!(
            "{:?}: Read buffer: {:?}",
            self.socket.peer_addr(),
            self.read_buf
        );
        for n in self.read_buf.iter().enumerate().filter_map(|(i, e)| {
            if *e == '\n' as u8 {
                Some(i)
            } else {
                None
            }
        }) {
            info!(
                "{:?}: Pos: {:?}; chunk: {:?}",
                self.socket.peer_addr(),
                n,
                &self.read_buf[prev..n]
            );
            let s = String::from_utf8_lossy(&self.read_buf[prev..n]).to_string();
            let cmd = MiChatCommand::Broadcast(s);
            info!("Send! {:?}", cmd);
            to_parent(cmd);
            prev = n + 1;
        }
        let remainder = self.read_buf[prev..].to_vec();
        info!(
            "{:?}: read Remainder: {}",
            self.socket.peer_addr(),
            remainder.len()
        );
        self.read_buf = remainder;
    }

    fn read(&mut self) -> Result<(), Error> {
        let mut abuf = vec![0; 1024];
        match self.socket.read(&mut abuf).context("Reading socket")? {
            0 => {
                info!("{:?}: EOF!", self.socket.peer_addr());
                self.read_eof = true;
                Ok(())
            }
            n => {
                info!("{:?}: Read {}bytes", self.socket.peer_addr(), n);
                self.read_buf.extend(&abuf[0..n]);
                Ok(())
            }
        }
    }

    fn write(&mut self) -> Result<(), Error> {
        let n = self
            .socket
            .write(&mut self.write_buf)
            .context("writing buffer")?;
        info!(
            "{:?}: Wrote {} of {} in buffer",
            self.socket.peer_addr(),
            n,
            self.write_buf.len()
        );
        self.write_buf = self.write_buf[n..].to_vec();
        info!(
            "{:?}: Now {:?}b",
            self.socket.peer_addr(),
            self.write_buf.len()
        );
        Ok(())
    }

    fn reregister(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut flags = mio::Ready::readable();
        if !self.write_buf.is_empty() {
            flags.insert(mio::Ready::writable());
        }
        info!("Registering {:?} with {:?}", self, flags);

        event_loop
            .reregister(&self.socket, self.token, flags, mio::PollOpt::oneshot())
            .context("EventLoop#reregister")?;

        Ok(())
    }

    fn enqueue(&mut self, s: &str) {
        self.write_buf.extend(s.as_bytes());
        self.write_buf.push('\n' as u8);
    }
}

#[derive(Debug)]
struct Listener {
    listener: TcpListener,
    sock_status: mio::Ready,
    token: mio::Token,
}

impl Listener {
    fn new(listener: TcpListener, token: mio::Token) -> Listener {
        Listener {
            listener: listener,
            sock_status: mio::Ready::empty(),
            token: token,
        }
    }
    fn reregister(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .reregister(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
            Ok(())
    }
}

impl RuleHandler for Listener {
    fn register(&self, event_loop: &mut mio::Poll, token: mio::Token) -> Result<(), Error> {
        event_loop
            .register(
                &self.listener,
                token,
                mio::Ready::readable(),
                mio::PollOpt::edge() | mio::PollOpt::oneshot(),
            ).context("Register listener")?;
            Ok(())
    }

    fn handle_event(&mut self, _event_loop: &mut mio::Poll, events: mio::Ready) {
        assert!(events.is_readable());
        self.sock_status.insert(events);
        info!(
            "Listener::handle_event: {:?}; this time: {:?}; now: {:?}",
            self.listener.local_addr(),
            events,
            self.sock_status
        );
    }
    fn process_rules(
        &mut self,
        event_loop: &mut mio::Poll,
        to_parent: &mut FnMut(MiChatCommand),
    ) -> Result<(), Error> {
        if self.sock_status.is_readable() {
            info!("the listener socket is ready to accept a connection");
            self.sock_status.remove(mio::Ready::readable());
            match self.listener.accept().context("Accept listening socket")? {
                (socket, _client) => {
                    let cmd = MiChatCommand::NewConnection(socket);
                    to_parent(cmd);
                }
            }

            self.reregister(event_loop, self.token)?;
        }
        Ok(())
    }

    fn is_closed(&self) -> bool {
        false
    }
}

#[derive(Debug)]
enum EventHandler {
    Listener(Listener),
    Conn(Connection),
}

impl ops::Deref for EventHandler {
    type Target = RuleHandler;
    fn deref(&self) -> &Self::Target {
        match self {
            &EventHandler::Conn(ref conn) => conn,
            &EventHandler::Listener(ref listener) => listener,
        }
    }
}
impl ops::DerefMut for EventHandler {
    fn deref_mut(&mut self) -> &mut Self::Target {
        match self {
            &mut EventHandler::Conn(ref mut conn) => conn,
            &mut EventHandler::Listener(ref mut listener) => listener,
        }
    }
}

impl MiChat {
    fn ready(&mut self, event_loop: &mut mio::Poll, token: mio::Token, events: mio::Ready) {
        info!("{:?}: {:?}", token, events);
        self.connections[token.into()].handle_event(event_loop, events);
        if self.connections[token.into()].is_closed() {
            info!("Removing; {:?}", token);
            self.connections.remove(token.into());
        }
    }

    fn process(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut parent_actions = VecDeque::new();
        let mut failed = Vec::new();
        loop {
            for (idx, conn) in self.connections.iter_mut() {
                if let Err(e) =
                    conn.process_rules(event_loop, &mut |action| parent_actions.push_back(action))
                {
                    failed.push(idx);
                    error!("Error on connection {:?}; Dropping: {:?}", idx, e);
                }
            }
            for idx in failed.drain(..) {
                self.connections.remove(idx);
            }
            // Anything left to process?
            if parent_actions.is_empty() {
                break;
            }

            for action in parent_actions.drain(..) {
                self.process_action(action, event_loop);
            }
        }

        Ok(())
    }

    fn run(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        loop {
            self.run_once(event_loop)?;
        }
    }
    fn run_once(&mut self, event_loop: &mut mio::Poll) -> Result<(), Error> {
        let mut events = mio::Events::with_capacity(1024);
        event_loop.poll(&mut events, None)?;
        for ev in &events {
            self.ready(event_loop, ev.token(), ev.readiness())
        }

        self.process(event_loop)
    }
}

fn main() {
    env_logger::init();
    let matches = App::new("michat")
        .arg(
            Arg::with_name("bind")
                .short("l")
                .takes_value(true)
                .required(true),
        ).get_matches();

    let address = value_t_or_exit!(matches.value_of("bind"), std::net::SocketAddr);
    let listener = TcpListener::bind(&address).expect("bind");

    let mut event_loop = mio::Poll::new().expect("Create event loop");
    let mut service = MiChat::new();

    service.listen(&mut event_loop, listener).expect("Starting listener");
    info!("running michat listener at: {:?}", address);
    service.run(&mut event_loop).expect("run");
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate serde;
extern crate rustc_serialize;
extern crate toml;
#[macro_use]
extern crate log;
extern crate env_logger;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hubcaps::{Credentials, Github, IssueOptions};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

macro_rules! panic_log {
    ($($arg:tt)*) => ( {
        error!($($arg)*);
        panic!()
    } );
}

trait ExpectLog {
    type Item;
    fn expect_log(self, &str) -> Self::Item;
}

impl<T> ExpectLog for Option<T> {
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            None => panic_log!("{}", msg),
            Some(s) => s,
        }
    }
}

impl<T, E> ExpectLog for Result<T, E>
    where E: std::fmt::Display
{
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            Err(err) => panic_log!("{} {}", msg, err),
            Ok(s) => s,
        }
    }
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name)
        .expect_log(&format!("Couldn't open config file {}", file_name));
    let mut str = String::new();
    config_file.read_to_string(&mut str)
        .expect_log(&format!("Couldn't read config file {}", file_name));
    let toml: Option<ConfigData> = decode_str(&str);
    toml.expect_log(&format!("Couldn't decode config file {}", file_name))
}

#[test]
fn it_reads_toml() {
    env_logger::init().unwrap();
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    env_logger::init().unwrap();

    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(&pull_request.commits_url, &repository.full_name)
                }
            }
            _ => info!("Recived a request that wasn't a pull-request webhook"),
        }
    });

    let ip_and_port = get_ip_and_port();

    match Server::http(ip_and_port.as_str()) {
        Err(err) => error!("ERROR: failed to start server, the error was: {}", err),
        Ok(server) => {
            match server.handle(hub) {
                Err(err) => error!("ERROR starting handler, the error was: {}", err),
                Ok(_) => info!("Successfully started server"),
            }
        }
    }
}

fn env_or(name: &str, def: &str) -> String {
    env::var_os(name).and_then(|v| v.into_string().ok()).unwrap_or(def.to_owned())
}

fn get_ip_and_port() -> String {
    let ip = env_or("STEVE_IP", "0.0.0.0");
    let port = env_or("STEVE_PORT", "80");
    format!("{}:{}", ip, port)
}

fn handle_pr(commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
        .expect_log("Missing github token, STEVE_GITHUB_TOKEN is not defined!")
        .into_string()
        .ok()
        .expect_log("Github token contains illegal characters!");
    match config_data.repos.get(&repository.to_owned()) {
        Some(config) => {
            update_issues(&commits_url,
                          &auth_token,
                          &config,
                          &config_data.api_root,
                          &repository)
        }
        None => info!("No config found for repo {}, skipping", repository),
    }
}

fn update_issues(commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let commits = client.get(commits_url).headers(headers).send().unwrap();
    assert_eq!(commits.status, hyper::Ok); //replace with actual error handling

    match serde_json::from_reader(commits) {
        Ok(json) => {
            match parse_commit_data(&json) {
                Ok(messages) => {
                    let mut issues = HashSet::new();
                    for message in messages {
                        get_issues(&mut issues, &message)
                    }

                    if !issues.is_empty() {
                        let github = Github::host(api_root,
                                                  "steve",
                                                  &client,
                                                  Credentials::Token(auth_token.to_owned()));
                        let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
                            strs => (strs[0], strs[1]),
                        };
                        let repo = github.repo(repo_name, owner);
                        match repo.issues().list(&Default::default()) {
                            Err(err) => error!("{:?}", err),
                            Ok(issue_data) => {
                                for issue in issues {
                                    if issue_data.len() as u64 <= issue {
                                        info!("Wanted to update issue #{}, but {} doesn't have an \
                                               issue with that number",
                                              issue,
                                              repository)
                                    } else {
                                        let ref current_flags = issue_data[issue as usize].labels;
                                        let new_flags: Vec<String> = current_flags.into_iter()
                                            .map(|l| l.name.clone())
                                            .chain(config.qa_flags.clone().into_iter())
                                            .collect();
                                        let issue_options =
                                            IssueOptions::new::<String,
                                                                String,
                                                                String,
                                                                String>(None,
                                                                        None,
                                                                        Some(config.qa_user
                                                                            .clone()),
                                                                        None,
                                                                        Some(new_flags));
                                        if let Err(err) = repo.issue(issue).edit(&issue_options) {
                                            error!("{:?}", err)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                Err(err) => error!("{:?}", err),
            }
        }
        Err(err) => error!("{:?}", err),
    }
}

#[test]
fn it_parses_commit_data() {
    let file = File::open("test-data/test-messages.json").expect("Couldn't open message file");
    let json = serde_json::from_reader(file);
    assert!(json.is_ok());
    let json = json.unwrap();
    let messages = parse_commit_data(&json);
    assert!(messages.is_ok());
    let messages = messages.unwrap();
    assert_eq!(messages[0], "message1");
    assert_eq!(messages[1], "swordfish");
}

fn parse_commit_data<'a>(json: &'a Value) -> Result<Vec<&'a str>, &str> {
    let arr = try!(json.as_array().ok_or("expected array as top level object"));
    arr.iter()
        .map(|obj| {
            obj.pointer("/commit/message")
                .ok_or("missing message field")
                .and_then(|m| m.as_string().ok_or("message field has wrong type"))
        })
        .collect()
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

#[test]
fn it_finds_issue_numbers() {
    let message = r"This is a commit message
        needs qa: #72 should get caught
        needs QA #333 should also get caught
        but #44 won't, and needs QA: #33 #45
        only gets #33. Duplicates are discarded:
        needs qa #72";
    let mut expected: HashSet<u64> = HashSet::new();
    expected.insert(72);
    expected.insert(333);
    expected.insert(33);
    let mut numbers: HashSet<u64> = HashSet::new();
    get_issues(&mut numbers, message);
    assert_eq!(numbers, expected);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").expect_log("{}");
    }
    for num in RE.captures_iter(message).filter_map(|cap| cap.at(1)) {
        numbers.insert(num.parse::<u64>().expect_log("{}"));
    }
}
<commit_msg>Handle http failures a bit better.<commit_after>#[macro_use]
extern crate lazy_static;
extern crate regex;
extern crate hyper;
extern crate hubcaps;
extern crate afterparty;
extern crate serde_json;
extern crate serde;
extern crate rustc_serialize;
extern crate toml;
#[macro_use]
extern crate log;
extern crate env_logger;

use std::collections::{HashMap, HashSet};
use std::fs::File;
use std::env;
use std::io::Read;

use hyper::{Client, Server, Url};
use hyper::header::{Headers, Authorization, Bearer, UserAgent};
use hyper::client::Response;
use hubcaps::{Credentials, Github, IssueOptions};
use afterparty::*;
use serde_json::Value;
use regex::Regex;
use toml::decode_str;

#[derive(RustcEncodable, RustcDecodable)]
struct RepoData {
    pub qa_user: String,
    pub qa_flags: Vec<String>,
}

#[derive(RustcEncodable, RustcDecodable)]
struct ConfigData {
    pub api_root: String,
    pub repos: HashMap<String, RepoData>,
}

macro_rules! panic_log {
    ($($arg:tt)*) => ( {
        error!($($arg)*);
        panic!()
    } );
}

trait ExpectLog {
    type Item;
    fn expect_log(self, &str) -> Self::Item;
}

impl<T> ExpectLog for Option<T> {
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            None => panic_log!("{}", msg),
            Some(s) => s,
        }
    }
}

impl<T, E> ExpectLog for Result<T, E>
    where E: std::fmt::Display
{
    type Item = T;
    fn expect_log(self, msg: &str) -> T {
        match self {
            Err(err) => panic_log!("{} {}", msg, err),
            Ok(s) => s,
        }
    }
}

fn read_config_file(file_name: &str) -> ConfigData {
    let mut config_file = File::open(file_name)
        .expect_log(&format!("Couldn't open config file {}", file_name));
    let mut str = String::new();
    config_file.read_to_string(&mut str)
        .expect_log(&format!("Couldn't read config file {}", file_name));
    let toml: Option<ConfigData> = decode_str(&str);
    toml.expect_log(&format!("Couldn't decode config file {}", file_name))
}

#[test]
fn it_reads_toml() {
    env_logger::init().unwrap();
    let config_data = read_config_file("test-data/test.toml");
    assert_eq!(config_data.api_root, "github.ibm.com/api/v3");
    let steve = config_data.repos.get("lkgrele/steve").unwrap();
    assert_eq!(steve.qa_user, "leif");
    assert_eq!(steve.qa_flags, ["qa", "test"]);
    let raul = config_data.repos.get("raul/robot").unwrap();
    assert_eq!(raul.qa_user, "raul");
    assert_eq!(raul.qa_flags, ["qa"]);
}

fn main() {
    env_logger::init().unwrap();

    let mut hub = Hub::new();
    hub.handle("pull_request", |delivery: &Delivery| {
        match delivery.payload {
            Event::PullRequest { ref pull_request, ref repository, .. } => {
                if pull_request.merged {
                    handle_pr(&pull_request.commits_url, &repository.full_name)
                }
            }
            _ => info!("Recived a request that wasn't a pull-request webhook"),
        }
    });

    let ip_and_port = get_ip_and_port();

    match Server::http(ip_and_port.as_str()) {
        Err(err) => error!("ERROR: failed to start server, the error was: {}", err),
        Ok(server) => {
            match server.handle(hub) {
                Err(err) => error!("ERROR starting handler, the error was: {}", err),
                Ok(_) => info!("Successfully started server"),
            }
        }
    }
}

fn env_or(name: &str, def: &str) -> String {
    env::var_os(name).and_then(|v| v.into_string().ok()).unwrap_or(def.to_owned())
}

fn get_ip_and_port() -> String {
    let ip = env_or("STEVE_IP", "0.0.0.0");
    let port = env_or("STEVE_PORT", "80");
    format!("{}:{}", ip, port)
}

fn handle_pr(commits_url: &str, repository: &str) {
    let config_data = read_config_file(".steve");
    let auth_token = env::var_os("STEVE_GITHUB_TOKEN")
        .expect_log("Missing github token, STEVE_GITHUB_TOKEN is not defined!")
        .into_string()
        .ok()
        .expect_log("Github token contains illegal characters!");
    match config_data.repos.get(&repository.to_owned()) {
        Some(config) => {
            update_issues(&commits_url,
                          &auth_token,
                          &config,
                          &config_data.api_root,
                          &repository)
        }
        None => info!("No config found for repo {}, skipping", repository),
    }
}

fn get_with_retries(client: &Client,
                    url: &str,
                    headers: &Headers,
                    max_retries: u64)
                    -> Result<Response, String> {
    for i in 1..max_retries {
        let retry_headers = headers.clone();
        match client.get(url).headers(retry_headers).send() {
            Ok(result) => {
                if result.status == hyper::Ok {
                    return Ok(result);
                } else {
                    if i < max_retries {
                        info!("http response {:?}, retrying", result.status_raw());
                    } else {
                        return Err(format!("http error {:?}", result.status_raw()));
                    }
                }
            }
            Err(err) => {
                if i < max_retries {
                    error!("Hyper error {}, retrying anyway", err)
                } else {
                    return Err(format!("Hyper error {}", err));
                }
            }
        }
    }
    unreachable!();
}

fn update_issues(commits_url: &str,
                 auth_token: &str,
                 config: &RepoData,
                 api_root: &str,
                 repository: &str) {
    let client = Client::new();
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let retries = env_or("STEVE_MAX_RETRIES", "5")
        .parse::<u64>()
        .expect_log("max retries need to be an integer");
    let commits = match get_with_retries(&client, &commits_url, &headers, retries) {
        Ok(c) => c,
        Err(err) => {
            error!("Failed to get commits, error {}", err);
            return ();
        }
    };

    match serde_json::from_reader(commits) {
        Ok(json) => {
            match parse_commit_data(&json) {
                Ok(messages) => {
                    let mut issues = HashSet::new();
                    for message in messages {
                        get_issues(&mut issues, &message)
                    }

                    if !issues.is_empty() {
                        let github = Github::host(api_root,
                                                  "steve",
                                                  &client,
                                                  Credentials::Token(auth_token.to_owned()));
                        let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
                            strs => (strs[0], strs[1]),
                        };
                        let repo = github.repo(repo_name, owner);
                        match repo.issues().list(&Default::default()) {
                            Err(err) => error!("{:?}", err),
                            Ok(issue_data) => {
                                for issue in issues {
                                    if issue_data.len() as u64 <= issue {
                                        info!("Wanted to update issue #{}, but {} doesn't have an \
                                               issue with that number",
                                              issue,
                                              repository)
                                    } else {
                                        let ref current_flags = issue_data[issue as usize].labels;
                                        let new_flags: Vec<String> = current_flags.into_iter()
                                            .map(|l| l.name.clone())
                                            .chain(config.qa_flags.clone().into_iter())
                                            .collect();
                                        let issue_options =
                                            IssueOptions::new::<String,
                                                                String,
                                                                String,
                                                                String>(None,
                                                                        None,
                                                                        Some(config.qa_user
                                                                            .clone()),
                                                                        None,
                                                                        Some(new_flags));
                                        if let Err(err) = repo.issue(issue).edit(&issue_options) {
                                            error!("{:?}", err)
                                        }
                                    }
                                }
                            }
                        }
                    }
                }
                Err(err) => error!("{:?}", err),
            }
        }
        Err(err) => error!("{:?}", err),
    }
}

#[test]
fn it_parses_commit_data() {
    let file = File::open("test-data/test-messages.json").expect("Couldn't open message file");
    let json = serde_json::from_reader(file);
    assert!(json.is_ok());
    let json = json.unwrap();
    let messages = parse_commit_data(&json);
    assert!(messages.is_ok());
    let messages = messages.unwrap();
    assert_eq!(messages[0], "message1");
    assert_eq!(messages[1], "swordfish");
}

fn parse_commit_data<'a>(json: &'a Value) -> Result<Vec<&'a str>, &str> {
    let arr = try!(json.as_array().ok_or("expected array as top level object"));
    arr.iter()
        .map(|obj| {
            obj.pointer("/commit/message")
                .ok_or("missing message field")
                .and_then(|m| m.as_string().ok_or("message field has wrong type"))
        })
        .collect()
}

fn update_issue(client: &Client,
                auth_token: &str,
                issue_num: u64,
                config: &RepoData,
                repository: &str,
                api_root: &str) {
    let github = Github::host(api_root,
                              "steve",
                              client,
                              Credentials::Token(auth_token.to_owned()));
    let (owner, repo_name) = match repository.split("/").collect::<Vec<_>>() {
        strs => (strs[0], strs[1]),
    };
    let _ = github.repo(repo_name, owner)
        .issue(issue_num)
        .labels()
        .add(config.qa_flags.iter().map(String::as_str).collect());
    let mut headers = Headers::new();
    headers.set(Authorization(Bearer { token: auth_token.to_owned() }));
    headers.set(UserAgent("steve".to_owned()));
    let update_json = format!("{{\"assignee\":\"{}\"}}", config.qa_user);
    let url = Url::parse(&format!("{}/repos/{}/issues/{}", api_root, repository, issue_num))
        .expect("bad url");
    let res = client.patch(url)
        .body(&update_json)
        .headers(headers.to_owned())
        .send()
        .unwrap();
    assert_eq!(res.status, hyper::Ok);
}

#[test]
fn it_finds_issue_numbers() {
    let message = r"This is a commit message
        needs qa: #72 should get caught
        needs QA #333 should also get caught
        but #44 won't, and needs QA: #33 #45
        only gets #33. Duplicates are discarded:
        needs qa #72";
    let mut expected: HashSet<u64> = HashSet::new();
    expected.insert(72);
    expected.insert(333);
    expected.insert(33);
    let mut numbers: HashSet<u64> = HashSet::new();
    get_issues(&mut numbers, message);
    assert_eq!(numbers, expected);
}

fn get_issues(numbers: &mut HashSet<u64>, message: &str) {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"(?i)needs\sqa:?\s#(\d+)").expect_log("{}");
    }
    for num in RE.captures_iter(message).filter_map(|cap| cap.at(1)) {
        numbers.insert(num.parse::<u64>().expect_log("{}"));
    }
}
<|endoftext|>


<commit_before>#![allow(dead_code)]

// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]

#[macro_use] extern crate error_chain;
#[macro_use] extern crate glium;
extern crate rand;

#[cfg(test)]
#[macro_use]
mod test_utils;

mod drawer;
mod errors;
mod graph;
mod map;
mod math;
mod mouse;
mod square;
mod state;
mod timer;
mod visible_graph;

use drawer::Drawer;
use graph::Graph;
use map::{Map, Player};
use math::{apply, compose};
use mouse::Mouse;
use square::SquareGrid;
use state::{MAX_GOOP, Occupied, State};
use visible_graph::GraphPt;

use glium::glutin::{Event, ElementState, MouseButton, VirtualKeyCode};
use glium::Surface;

use std::rc::Rc;
use std::time::{Duration, Instant};

// This only gives access within this module. Make this `pub use errors::*;`
// instead if the types must be accessible from other modules (e.g., within
// a `links` section).
use errors::*;

fn main() {
    if let Err(ref e) = run() {
        use ::std::io::Write;
        let stderr = &mut ::std::io::stderr();
        let errmsg = "Error writing to stderr";

        writeln!(stderr, "error: {}", e).expect(errmsg);

        for e in e.iter().skip(1) {
            writeln!(stderr, "caused by: {}", e).expect(errmsg);
        }

        // The backtrace is not always generated. Try to run this example
        // with `RUST_BACKTRACE=1`.
        if let Some(backtrace) = e.backtrace() {
            writeln!(stderr, "backtrace: {:?}", backtrace).expect(errmsg);
        }

        ::std::process::exit(1);
    }
}

fn run() -> Result<()> {
    use glium::DisplayBuild;

    let display = glium::glutin::WindowBuilder::new()
        .with_title("rbattle".to_string())
        .build_glium()
        .chain_err(|| "unable to open window")?;

    let graph = SquareGrid::new(15, 15);
    let sources = vec![16, 45];
    let colors = vec![(0x9f, 0x20, 0xb1), (0xb1, 0x20, 0x44),
                      (0x20, 0xb1, 0x21), (0x20, 0x67, 0xb1),
                      (0xe0, 0x6f, 0x3a)];
    let map = Rc::new(Map::new(graph, sources, colors));
    let drawer = Drawer::new(&display, &map)
        .chain_err(|| "failed to construct Drawer for map")?;

    let mut state = State::new(map.clone());

    state.nodes[45] = Some(Occupied {
        player: Player(2),
        outflows: map.graph.neighbors(45),
        goop: MAX_GOOP
    });

    state.nodes[30] = Some(Occupied {
        player: Player(2),
        outflows: vec![15],
        goop: 0
    });

    state.nodes[16] = Some(Occupied {
        player: Player(0),
        outflows: map.graph.neighbors(16),
        goop: MAX_GOOP
    });

    state.nodes[17] = Some(Occupied {
        player: Player(1),
        outflows: vec![],
        goop: 2
    });

    let mut mouse = Mouse::new(map.clone());

    let mut turn = 0;
    let start = Instant::now();

    // True if we should run a game step on every frame.
    let mut free_running = false;

    loop {
        // It seems like glium always makes a frame take a full 16ms, regardless
        // of how much work we ask it to do, but I don't see anything in the
        // documentation about this. We're leaning on that for now to keep
        // timing consistent, but we'll need to add something to control timing
        // explicitly to avoid depending on this behavior.
        let mut frame = display.draw();
        frame.clear_color(1.0, 1.0, 1.0, 1.0);
        let status = drawer.draw(&mut frame, &state, &mouse);
        frame.finish()
            .chain_err(|| "drawing finish failed")?;

        let window_to_game = status?;
        let window_to_graph = compose(map.game_to_graph, window_to_game);

        let mut single_step = false;
        for event in display.poll_events() {
            match event {
                Event::Closed => return Ok(()),
                Event::KeyboardInput(ElementState::Pressed, _,
                                     Some(VirtualKeyCode::Space)) => {
                    free_running = false;
                    single_step = true;
                }
                Event::KeyboardInput(ElementState::Pressed, _,
                                     Some(VirtualKeyCode::Return)) => {
                    free_running = true;
                }
                Event::MouseMoved(x, y) => {
                    let graph_pos = apply(window_to_graph, [x as f32, y as f32]);
                    mouse.move_to(GraphPt(graph_pos));
                }
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    mouse.click();
                }
                Event::MouseInput(ElementState::Released, MouseButton::Left) => {
                    if let Some(action) = mouse.release() {
                        state.take_action(action);
                    }
                }
                _ => ()
            }
        }

        if free_running || single_step {
            println!("Turn {} at {:9.3}s:", turn, elapsed_since(&start));

            let start_generation = Instant::now();
            state.advance();
            println!("    advance to next state: {:9.6}s:", elapsed_since(&start_generation));
            turn += 1;
        }
    }
}

/// Return the number of seconds that have elasped since `instant`. Partial
/// seconds are returned as fractional values.
fn elapsed_since(instant: &Instant) -> f64 {
    let elapsed = instant.elapsed();
    elapsed.as_secs() as f64 +
        (elapsed.subsec_nanos() as f64 / 1e9)
}
<commit_msg>Ditch unused import.<commit_after>#![allow(dead_code)]

// `error_chain!` can recurse deeply
#![recursion_limit = "1024"]

#[macro_use] extern crate error_chain;
#[macro_use] extern crate glium;
extern crate rand;

#[cfg(test)]
#[macro_use]
mod test_utils;

mod drawer;
mod errors;
mod graph;
mod map;
mod math;
mod mouse;
mod square;
mod state;
mod timer;
mod visible_graph;

use drawer::Drawer;
use graph::Graph;
use map::{Map, Player};
use math::{apply, compose};
use mouse::Mouse;
use square::SquareGrid;
use state::{MAX_GOOP, Occupied, State};
use visible_graph::GraphPt;

use glium::glutin::{Event, ElementState, MouseButton, VirtualKeyCode};
use glium::Surface;

use std::rc::Rc;
use std::time::Instant;

// This only gives access within this module. Make this `pub use errors::*;`
// instead if the types must be accessible from other modules (e.g., within
// a `links` section).
use errors::*;

fn main() {
    if let Err(ref e) = run() {
        use ::std::io::Write;
        let stderr = &mut ::std::io::stderr();
        let errmsg = "Error writing to stderr";

        writeln!(stderr, "error: {}", e).expect(errmsg);

        for e in e.iter().skip(1) {
            writeln!(stderr, "caused by: {}", e).expect(errmsg);
        }

        // The backtrace is not always generated. Try to run this example
        // with `RUST_BACKTRACE=1`.
        if let Some(backtrace) = e.backtrace() {
            writeln!(stderr, "backtrace: {:?}", backtrace).expect(errmsg);
        }

        ::std::process::exit(1);
    }
}

fn run() -> Result<()> {
    use glium::DisplayBuild;

    let display = glium::glutin::WindowBuilder::new()
        .with_title("rbattle".to_string())
        .build_glium()
        .chain_err(|| "unable to open window")?;

    let graph = SquareGrid::new(15, 15);
    let sources = vec![16, 45];
    let colors = vec![(0x9f, 0x20, 0xb1), (0xb1, 0x20, 0x44),
                      (0x20, 0xb1, 0x21), (0x20, 0x67, 0xb1),
                      (0xe0, 0x6f, 0x3a)];
    let map = Rc::new(Map::new(graph, sources, colors));
    let drawer = Drawer::new(&display, &map)
        .chain_err(|| "failed to construct Drawer for map")?;

    let mut state = State::new(map.clone());

    state.nodes[45] = Some(Occupied {
        player: Player(2),
        outflows: map.graph.neighbors(45),
        goop: MAX_GOOP
    });

    state.nodes[30] = Some(Occupied {
        player: Player(2),
        outflows: vec![15],
        goop: 0
    });

    state.nodes[16] = Some(Occupied {
        player: Player(0),
        outflows: map.graph.neighbors(16),
        goop: MAX_GOOP
    });

    state.nodes[17] = Some(Occupied {
        player: Player(1),
        outflows: vec![],
        goop: 2
    });

    let mut mouse = Mouse::new(map.clone());

    let mut turn = 0;
    let start = Instant::now();

    // True if we should run a game step on every frame.
    let mut free_running = false;

    loop {
        // It seems like glium always makes a frame take a full 16ms, regardless
        // of how much work we ask it to do, but I don't see anything in the
        // documentation about this. We're leaning on that for now to keep
        // timing consistent, but we'll need to add something to control timing
        // explicitly to avoid depending on this behavior.
        let mut frame = display.draw();
        frame.clear_color(1.0, 1.0, 1.0, 1.0);
        let status = drawer.draw(&mut frame, &state, &mouse);
        frame.finish()
            .chain_err(|| "drawing finish failed")?;

        let window_to_game = status?;
        let window_to_graph = compose(map.game_to_graph, window_to_game);

        let mut single_step = false;
        for event in display.poll_events() {
            match event {
                Event::Closed => return Ok(()),
                Event::KeyboardInput(ElementState::Pressed, _,
                                     Some(VirtualKeyCode::Space)) => {
                    free_running = false;
                    single_step = true;
                }
                Event::KeyboardInput(ElementState::Pressed, _,
                                     Some(VirtualKeyCode::Return)) => {
                    free_running = true;
                }
                Event::MouseMoved(x, y) => {
                    let graph_pos = apply(window_to_graph, [x as f32, y as f32]);
                    mouse.move_to(GraphPt(graph_pos));
                }
                Event::MouseInput(ElementState::Pressed, MouseButton::Left) => {
                    mouse.click();
                }
                Event::MouseInput(ElementState::Released, MouseButton::Left) => {
                    if let Some(action) = mouse.release() {
                        state.take_action(action);
                    }
                }
                _ => ()
            }
        }

        if free_running || single_step {
            println!("Turn {} at {:9.3}s:", turn, elapsed_since(&start));

            let start_generation = Instant::now();
            state.advance();
            println!("    advance to next state: {:9.6}s:", elapsed_since(&start_generation));
            turn += 1;
        }
    }
}

/// Return the number of seconds that have elasped since `instant`. Partial
/// seconds are returned as fractional values.
fn elapsed_since(instant: &Instant) -> f64 {
    let elapsed = instant.elapsed();
    elapsed.as_secs() as f64 +
        (elapsed.subsec_nanos() as f64 / 1e9)
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate termios;

use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, winsize};
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::io::{self, Stdin, Stdout, Read, Error, ErrorKind, Write};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct RawMode {
    orig_term: Termios
}

struct Editor {
    _mode: RawMode,
    cx: u16,
    cy: u16,
    screenrows: u16,
    screencols: u16,
    stdin: Stdin,
    stdout: Stdout,
}

impl RawMode {
    fn enable_raw_mode() -> io::Result<RawMode> {
        let mut term = Termios::from_fd(STDIN_FILENO)?;
        let mode = RawMode { orig_term: term.clone() };

        term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        term.c_oflag &= !OPOST;
        term.c_cflag |= CS8;
        term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
        term.c_cc[VMIN] = 0;
        term.c_cc[VTIME] = 1;

        tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
        Ok(mode)
    }
}

impl Drop for RawMode {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

fn editor_read_key(stdin: &mut Stdin) -> u8 {
    let mut buf = [0; 1];
    loop {
        let n = stdin.read(&mut buf)
                 .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
                 .expect("read");
        if n == 1 {
            return buf[0];
        }
    }
}

fn get_window_size() -> io::Result<(u16, u16)> {
    let ws = winsize { ws_col: 0, ws_row: 0, ws_xpixel: 0, ws_ypixel: 0 };
    unsafe {
        if libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0 {
            return Err(Error::new(ErrorKind::Other, "get_window_size: ioctl failed"))
        }
    }
    Ok((ws.ws_row, ws.ws_col))
}

impl Editor {
    fn new() -> io::Result<Editor> {
        let mode = RawMode::enable_raw_mode()?;
        let (rows, cols) = get_window_size()?;
        let stdin = io::stdin();
        let stdout = io::stdout();
        Ok(Editor {
            _mode: mode,
            cx: 0,
            cy: 0,
            screenrows: rows,
            screencols: cols,
            stdin: stdin,
            stdout: stdout
        })
    }

    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }

    fn draw_rows(&mut self) -> io::Result<()> {
        for y in 0..(self.screenrows) {
            if y == self.screenrows / 3 {
                let mut msg = format!("Kilo-rs editor -- version {}", VERSION);
                msg.truncate(self.screencols as usize);
                let padding = (self.screencols - msg.len() as u16) / 2;
                if padding > 0 {
                    self.write(b"~")?;
                    for _ in 1..padding {
                        self.write(b" ")?;
                    }
                }
                self.write(msg.as_bytes())?;
            } else {
                self.write(b"~")?;
            }

            self.write(b"\x1b[K")?;
            if y < self.screenrows - 1 {
                self.write(b"\r\n")?;
            }
        }
        Ok(())
    }

    fn refresh_screen(&mut self) -> io::Result<()> {
        self.write(b"\x1b[?25l")?;
        self.write(b"\x1b[H")?;

        self.draw_rows()?;

        let move_cursor = format!("\x1b[{};{}H", self.cy + 1, self.cx + 1).into_bytes();
        self.write(&move_cursor)?;

        self.write(b"\x1b[?25h")?;
        self.flush()
    }

    fn move_cursor(&mut self, c: u8) {
        match c {
            b'w' => {
                self.cy -= 1;
            },
            b's' => {
                self.cy += 1;
            },
            b'a' => {
                self.cx -= 1;
            },
            b'd' => {
                self.cx += 1;
            },
            _ => {},
        }
    }

    fn process_keypress(&mut self) -> bool {
        let c = editor_read_key(&mut self.stdin);

        match c {
            k if k == ctrl_key!(b'q') => {
                false
            },
            b'w' | b's' | b'a' |  b'd' => {
                self.move_cursor(c);
                true
            },
            _ => true
        }
    }
}

impl Drop for Editor {
    fn drop(&mut self) {
        // clear screen
        self.stdout.write(b"\x1b[2J").unwrap();
        self.stdout.write(b"\x1b[H").unwrap();
        self.stdout.flush().unwrap();
    }
}

fn main() {
    let mut editor = Editor::new().unwrap();

    editor.refresh_screen().unwrap();
    while editor.process_keypress() {
        editor.refresh_screen().unwrap();
    }
}
<commit_msg>Arrow keys<commit_after>extern crate libc;
extern crate termios;

use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, winsize};
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::io::{self, Stdin, Stdout, Read, Error, ErrorKind, Write};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

const ARROW_LEFT: u32 = 1000;
const ARROW_RIGHT: u32 = 1001;
const ARROW_UP: u32 = 1002;
const ARROW_DOWN: u32 = 1003;

macro_rules! ctrl_key {
    ($k:expr) => (($k & 0x1f) as u32);
}

struct RawMode {
    orig_term: Termios
}

struct Editor {
    _mode: RawMode,
    cx: u16,
    cy: u16,
    screenrows: u16,
    screencols: u16,
    stdin: Stdin,
    stdout: Stdout,
}

impl RawMode {
    fn enable_raw_mode() -> io::Result<RawMode> {
        let mut term = Termios::from_fd(STDIN_FILENO)?;
        let mode = RawMode { orig_term: term.clone() };

        term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        term.c_oflag &= !OPOST;
        term.c_cflag |= CS8;
        term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
        term.c_cc[VMIN] = 0;
        term.c_cc[VTIME] = 1;

        tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
        Ok(mode)
    }
}

impl Drop for RawMode {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

fn read_non_blocking<R : Read>(r : &mut R, buf: &mut [u8]) -> usize {
    r.read(buf)
     .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
     .expect("read_non_blocking")
}

fn editor_read_key(stdin: &mut Stdin) -> u32 {
    let mut buf = [0; 1];
    loop {
        let n = read_non_blocking(stdin, &mut buf);
        if n == 1 {
            if buf[0] == b'\x1b' {
                let mut seq = [0; 2];
                let n = read_non_blocking(stdin, &mut seq);
                if n == 2 && seq[0] == b'[' {
                    return match seq[1] {
                        b'A' => ARROW_UP,
                        b'B' => ARROW_DOWN,
                        b'C' => ARROW_RIGHT,
                        b'D' => ARROW_LEFT,
                        _ => b'\x1b' as u32,
                    }
                } else {
                    return b'\x1b' as u32;
                }
            } else {
                return buf[0] as u32;
            }
        }
    }
}

fn get_window_size() -> io::Result<(u16, u16)> {
    let ws = winsize { ws_col: 0, ws_row: 0, ws_xpixel: 0, ws_ypixel: 0 };
    unsafe {
        if libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0 {
            return Err(Error::new(ErrorKind::Other, "get_window_size: ioctl failed"))
        }
    }
    Ok((ws.ws_row, ws.ws_col))
}

impl Editor {
    fn new() -> io::Result<Editor> {
        let mode = RawMode::enable_raw_mode()?;
        let (rows, cols) = get_window_size()?;
        let stdin = io::stdin();
        let stdout = io::stdout();
        Ok(Editor {
            _mode: mode,
            cx: 0,
            cy: 0,
            screenrows: rows,
            screencols: cols,
            stdin: stdin,
            stdout: stdout
        })
    }

    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }

    fn draw_rows(&mut self) -> io::Result<()> {
        for y in 0..(self.screenrows) {
            if y == self.screenrows / 3 {
                let mut msg = format!("Kilo-rs editor -- version {}", VERSION);
                msg.truncate(self.screencols as usize);
                let padding = (self.screencols - msg.len() as u16) / 2;
                if padding > 0 {
                    self.write(b"~")?;
                    for _ in 1..padding {
                        self.write(b" ")?;
                    }
                }
                self.write(msg.as_bytes())?;
            } else {
                self.write(b"~")?;
            }

            self.write(b"\x1b[K")?;
            if y < self.screenrows - 1 {
                self.write(b"\r\n")?;
            }
        }
        Ok(())
    }

    fn refresh_screen(&mut self) -> io::Result<()> {
        self.write(b"\x1b[?25l")?;
        self.write(b"\x1b[H")?;

        self.draw_rows()?;

        let move_cursor = format!("\x1b[{};{}H", self.cy + 1, self.cx + 1).into_bytes();
        self.write(&move_cursor)?;

        self.write(b"\x1b[?25h")?;
        self.flush()
    }

    fn move_cursor(&mut self, c: u32) {
        match c {
            ARROW_UP => {
                self.cy -= 1;
            },
            ARROW_DOWN => {
                self.cy += 1;
            },
            ARROW_LEFT => {
                self.cx -= 1;
            },
            ARROW_RIGHT => {
                self.cx += 1;
            },
            _ => {},
        }
    }

    fn process_keypress(&mut self) -> bool {
        let c = editor_read_key(&mut self.stdin);

        match c {
            k if k == ctrl_key!(b'q') => {
                false
            },
            ARROW_UP | ARROW_DOWN | ARROW_LEFT | ARROW_RIGHT => {
                self.move_cursor(c);
                true
            },
            _ => true
        }
    }
}

impl Drop for Editor {
    fn drop(&mut self) {
        // clear screen
        self.stdout.write(b"\x1b[2J").unwrap();
        self.stdout.write(b"\x1b[H").unwrap();
        self.stdout.flush().unwrap();
    }
}

fn main() {
    let mut editor = Editor::new().unwrap();

    editor.refresh_screen().unwrap();
    while editor.process_keypress() {
        editor.refresh_screen().unwrap();
    }
}
<|endoftext|>