<commit_before>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![recursion_limit = "1024"]
#[macro_use] extern crate error_chain;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate lazy_static;
extern crate regex;
extern crate toml;
extern crate url;

mod errors;

use errors::*;
use std::env;
use std::fs;
use std::io::Read;
use std::process::Command;
use url::Url;
use regex::Regex;

const CONFIG_FILE_NAME: &'static str = "url-handler.toml";

#[derive(Debug, Deserialize)]
struct Config {
    handler: Vec<Handler>
}

#[derive(Debug, Deserialize)]
struct Handler {
    scheme: String,
    command: String,
    args: Option<String>
}

fn load_config(cfg: &str) -> Result<Config> {
    let mut config_file = fs::File::open(cfg)
        .chain_err(|| format!("couldn't find {}", cfg))?;
    let mut contents = String::new();
    config_file.read_to_string(&mut contents)?;
    toml::from_str(&*contents).chain_err(|| "Could not load config file.")
}

fn run_command(cmd: &str, args: Vec<&str>) -> Result<i32> {
    let output = Command::new(cmd).args(args).output()?;
    Ok(output.status.code().unwrap_or(-1))
}

// Expand system environment variables, ie. %USERNAME%
fn expand_env(str: &str) -> Result<String> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"%([_\(\)\{\}\[\]\$\\;!\?0-9A-Za-z]+)%").unwrap();
    }
    let mut str_expanded = String::from(str);
    for cap in RE.captures_iter(&*str) {
        let var = &cap[0];
        let clear = var.trim_matches('%');
        let expanded = env::var(clear).chain_err(|| var)?;
        str_expanded = str_expanded.replace(var, &*expanded);
    }

    Ok(str_expanded)
}

// Expand named parameters: {key} -> value
fn expand_named(str: Option<String>, url: &Url) -> String {
    match str {
        Some(s) => {
            let params = url.query_pairs();
            let mut args = String::from(s);
            for (k, v) in params {
                args = str::replace(&*args, &*format!("{{{}}}", k), &*v);
            }
            args
        },
        None => String::new()
    }
}

// Expand arguments %0 %1 %2 ...
fn expand_args(str: &str, argv: Vec<&str>) -> String {
    let mut args = String::from(str);
    for (i, &item) in argv.iter().enumerate() {
        args = str::replace(&*args, &*format!("%{}", i + 1), &*item);
    }
    args
}

// Split a string containing space separated args into a vector considering quoted strings with spaces
fn split_args<'a>(args: &'a str) -> Vec<&'a str> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r#"[^\s"']+|"([^"]*)"|'([^']*)'"#).unwrap();
    }
    RE.captures_iter(args)
        .map(|cap| cap.get(0).unwrap().as_str().trim_matches('"'))
        .collect()
}

// Anything after the scheme and before parameters "?" is considered a numbered argument
fn get_args(url: &Url) -> Vec<&str> {
    if url.cannot_be_a_base() {
        vec![url.host_str().unwrap_or("").into(), url.path().into()]
    } else {
        vec![url.host_str().unwrap_or("").into()]
    }
}

fn run(arg: &str, cfg: &str) -> Result<i32> {
    let config = load_config(cfg)?;

    let url = Url::parse(arg)?;
    let scheme = url.scheme();

    let handler = config.handler.into_iter()
        .find(|it| it.scheme == scheme)
        .ok_or(ErrorKind::HandlerNotFound)?;

    let mut args = get_args(&url);
    let mut paths = url.path_segments().map(|c| c.collect::<Vec<_>>()).unwrap_or_else(|| vec![]);
    args.append(&mut paths);
    args.retain(|e| !e.is_empty());

    let args_expanded = expand_named(handler.args, &url);
    let args_expanded = expand_args(&*args_expanded, args);
    let args_expanded = expand_env(&*args_expanded)?;
    let cmd_expanded = &*expand_env(&*handler.command)?;

    //println!("{} {:?}", cmd_expanded, args_expanded);
    run_command(&*cmd_expanded, split_args(&*args_expanded))
}

fn main() {
    let matches = clap_app!(urlhandler =>
        (version: "0.1")
        (author: "Danny Angelo Carminati Grein <danny.cabelo@gmail.com>")
        (about: "Convert custom URL strings to command and command line arguments and execute them")
        (@arg URL: +required +takes_value "URL to handle, convert and execute")
        (@arg CONFIG: -c --config +takes_value "CONFIG file with handlers settings")
        (@arg install: -i --install "Install custom handles to the system")
    ).get_matches();

    if matches.is_present("install") {
        println!("install not implemented");
        ::std::process::exit(0);
    }

    let url_arg = matches.value_of("URL").unwrap(); // URL is required and will always be Some
    let config_file = matches.value_of("CONFIG").unwrap_or(CONFIG_FILE_NAME);

    let code = match run(url_arg, config_file) {
        Err(ref e) => {
            use ::std::io::Write;
            let stderr = &mut ::std::io::stderr();
            let err_msg = "Error writing to stderr";
            writeln!(stderr, "error: {}", e).expect(err_msg);

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

            // To enable backtrace run with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                writeln!(stderr, "backtrace: {:?}", backtrace).expect(err_msg);
            }
            -1
        }
        Ok(c) => c
    };

    ::std::process::exit(code);
}
<commit_msg>Remove unnecessary lifetime parameter.<commit_after>#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]
#![recursion_limit = "1024"]
#[macro_use] extern crate error_chain;
#[macro_use] extern crate clap;
#[macro_use] extern crate serde_derive;
#[macro_use] extern crate lazy_static;
extern crate regex;
extern crate toml;
extern crate url;

mod errors;

use errors::*;
use std::env;
use std::fs;
use std::io::Read;
use std::process::Command;
use url::Url;
use regex::Regex;

const CONFIG_FILE_NAME: &'static str = "url-handler.toml";

#[derive(Debug, Deserialize)]
struct Config {
    handler: Vec<Handler>
}

#[derive(Debug, Deserialize)]
struct Handler {
    scheme: String,
    command: String,
    args: Option<String>
}

fn load_config(cfg: &str) -> Result<Config> {
    let mut config_file = fs::File::open(cfg)
        .chain_err(|| format!("couldn't find {}", cfg))?;
    let mut contents = String::new();
    config_file.read_to_string(&mut contents)?;
    toml::from_str(&*contents).chain_err(|| "Could not load config file.")
}

fn run_command(cmd: &str, args: Vec<&str>) -> Result<i32> {
    let output = Command::new(cmd).args(args).output()?;
    Ok(output.status.code().unwrap_or(-1))
}

// Expand system environment variables, ie. %USERNAME%
fn expand_env(str: &str) -> Result<String> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"%([_\(\)\{\}\[\]\$\\;!\?0-9A-Za-z]+)%").unwrap();
    }
    let mut str_expanded = String::from(str);
    for cap in RE.captures_iter(&*str) {
        let var = &cap[0];
        let clear = var.trim_matches('%');
        let expanded = env::var(clear).chain_err(|| var)?;
        str_expanded = str_expanded.replace(var, &*expanded);
    }

    Ok(str_expanded)
}

// Expand named parameters: {key} -> value
fn expand_named(str: Option<String>, url: &Url) -> String {
    match str {
        Some(s) => {
            let params = url.query_pairs();
            let mut args = String::from(s);
            for (k, v) in params {
                args = str::replace(&*args, &*format!("{{{}}}", k), &*v);
            }
            args
        },
        None => String::new()
    }
}

// Expand arguments %0 %1 %2 ...
fn expand_args(str: &str, argv: Vec<&str>) -> String {
    let mut args = String::from(str);
    for (i, &item) in argv.iter().enumerate() {
        args = str::replace(&*args, &*format!("%{}", i + 1), &*item);
    }
    args
}

// Split a string containing space separated args into a vector considering quoted strings with spaces
fn split_args(args: &str) -> Vec<&str> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r#"[^\s"']+|"([^"]*)"|'([^']*)'"#).unwrap();
    }
    RE.captures_iter(args)
        .map(|cap| cap.get(0).unwrap().as_str().trim_matches('"'))
        .collect()
}

// Anything after the scheme and before parameters "?" is considered a numbered argument
fn get_args(url: &Url) -> Vec<&str> {
    if url.cannot_be_a_base() {
        vec![url.host_str().unwrap_or("").into(), url.path().into()]
    } else {
        vec![url.host_str().unwrap_or("").into()]
    }
}

fn run(arg: &str, cfg: &str) -> Result<i32> {
    let config = load_config(cfg)?;

    let url = Url::parse(arg)?;
    let scheme = url.scheme();

    let handler = config.handler.into_iter()
        .find(|it| it.scheme == scheme)
        .ok_or(ErrorKind::HandlerNotFound)?;

    let mut args = get_args(&url);
    let mut paths = url.path_segments().map(|c| c.collect::<Vec<_>>()).unwrap_or_else(|| vec![]);
    args.append(&mut paths);
    args.retain(|e| !e.is_empty());

    let args_expanded = expand_named(handler.args, &url);
    let args_expanded = expand_args(&*args_expanded, args);
    let args_expanded = expand_env(&*args_expanded)?;
    let cmd_expanded = &*expand_env(&*handler.command)?;

    //println!("{} {:?}", cmd_expanded, args_expanded);
    run_command(&*cmd_expanded, split_args(&*args_expanded))
}

fn main() {
    let matches = clap_app!(urlhandler =>
        (version: "0.1")
        (author: "Danny Angelo Carminati Grein <danny.cabelo@gmail.com>")
        (about: "Convert custom URL strings to command and command line arguments and execute them")
        (@arg URL: +required +takes_value "URL to handle, convert and execute")
        (@arg CONFIG: -c --config +takes_value "CONFIG file with handlers settings")
        (@arg install: -i --install "Install custom handles to the system")
    ).get_matches();

    if matches.is_present("install") {
        println!("install not implemented");
        ::std::process::exit(0);
    }

    let url_arg = matches.value_of("URL").unwrap(); // URL is required and will always be Some
    let config_file = matches.value_of("CONFIG").unwrap_or(CONFIG_FILE_NAME);

    let code = match run(url_arg, config_file) {
        Err(ref e) => {
            use ::std::io::Write;
            let stderr = &mut ::std::io::stderr();
            let err_msg = "Error writing to stderr";
            writeln!(stderr, "error: {}", e).expect(err_msg);

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

            // To enable backtrace run with `RUST_BACKTRACE=1`.
            if let Some(backtrace) = e.backtrace() {
                writeln!(stderr, "backtrace: {:?}", backtrace).expect(err_msg);
            }
            -1
        }
        Ok(c) => c
    };

    ::std::process::exit(code);
}
<|endoftext|>


<commit_before>extern crate byteorder;
extern crate cgmath;
extern crate env_logger;
extern crate getopts;
#[macro_use]
extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;
#[macro_use]
extern crate log;
extern crate progressive;
extern crate rustc_serialize;
extern crate ini;
extern crate toml;

mod config;
mod level;
mod model;
mod render;
mod splay;

use glutin::Event;

pub struct Camera {
    loc: cgmath::Vector3<f32>,
    rot: cgmath::Quaternion<f32>,
    proj: cgmath::PerspectiveFov<f32>,
}

impl Camera {
    pub fn get_view_proj(&self) -> cgmath::Matrix4<f32> {
        use cgmath::{Decomposed, Matrix4, Transform};
        let view = Decomposed {
            scale: 1.0,
            rot: self.rot,
            disp: self.loc,
        };
        let view_mx: Matrix4<f32> = view.inverse_transform().unwrap().into();
        let proj_mx: Matrix4<f32> = self.proj.into();
        proj_mx * view_mx
    }
}

trait App<R: gfx::Resources> {
    fn on_event<F: gfx::Factory<R>>(&mut self, Event, &mut F) -> bool;
    fn on_frame<C: gfx::CommandBuffer<R>>(&mut self, &mut gfx::Encoder<R, C>);
}

struct GameApp<R: gfx::Resources> {
    render: render::Render<R>,
    cam: Camera,
}

impl<R: gfx::Resources> GameApp<R> {
    fn new<F: gfx::Factory<R>>(settings: &config::Settings,
           output: gfx::handle::RenderTargetView<R, render::ColorFormat>,
           factory: &mut F) -> GameApp<R>
    {
        info!("Loading world parameters");
        let config = settings.get_level();
        let lev = level::load(&config);

        GameApp {
            render: render::init(factory, output, &lev),
            cam: Camera {
                loc: cgmath::vec3(0.0, 0.0, 200.0),
                rot: cgmath::Quaternion::new(1.0, 0.0, 0.0, 0.0),
                proj: cgmath::PerspectiveFov {
                    fovy: cgmath::deg(45.0).into(),
                    aspect: settings.get_screen_aspect(),
                    near: 1.0,
                    far: 100000.0,
                },
            },
        }
    }
}

impl<R: gfx::Resources> App<R> for GameApp<R> {
    fn on_event<F: gfx::Factory<R>>(&mut self, event: Event, factory: &mut F) -> bool {
        use cgmath::Rotation3;
        use glutin::VirtualKeyCode as Key;
        let delta = cgmath::rad(0.05);
        let step = 10.0;
        match event {
            Event::KeyboardInput(_, _, Some(Key::Escape)) |
            Event::Closed => return false,
            Event::KeyboardInput(_, _, Some(Key::L)) => self.render.reload(factory),
            Event::KeyboardInput(_, _, Some(Key::W)) =>
                self.cam.loc = self.cam.loc + cgmath::vec3(0.0, step, 0.0),
            Event::KeyboardInput(_, _, Some(Key::S)) =>
                self.cam.loc = self.cam.loc - cgmath::vec3(0.0, step, 0.0),
            Event::KeyboardInput(_, _, Some(Key::R)) =>
                self.cam.rot = self.cam.rot * cgmath::Quaternion::from_axis_angle(cgmath::vec3(1.0, 0.0, 0.0), delta),
            Event::KeyboardInput(_, _, Some(Key::F)) =>
                self.cam.rot = self.cam.rot * cgmath::Quaternion::from_axis_angle(cgmath::vec3(1.0, 0.0, 0.0), -delta),
            Event::KeyboardInput(_, _, Some(Key::A)) =>
                self.cam.rot = cgmath::Quaternion::from_axis_angle(cgmath::vec3(0.0, 0.0, 1.0), delta) * self.cam.rot,
            Event::KeyboardInput(_, _, Some(Key::D)) =>
                self.cam.rot = cgmath::Quaternion::from_axis_angle(cgmath::vec3(0.0, 0.0, 1.0), -delta) * self.cam.rot,
            _ => {},
        }
        true
    }
    fn on_frame<C: gfx::CommandBuffer<R>>(&mut self, enc: &mut gfx::Encoder<R, C>) {
        self.render.draw(enc, &self.cam);
    }
}

struct ObjectViewApp<R: gfx::Resources> {
    bundle: gfx::Bundle<R, render::object::Data<R>>,
    cam: Camera,
}

impl<R: gfx::Resources> ObjectViewApp<R> {
    fn new<F: gfx::Factory<R>>(path: &str, settings: &config::Settings,
           output: gfx::handle::RenderTargetView<R, render::ColorFormat>,
           factory: &mut F) -> ObjectViewApp<R>
    {
        use std::io::BufReader;
        use std::fs::File;
        use gfx::traits::FactoryExt;

        let pal_data = level::load_palette(&settings.get_object_palette_path());

        let mut file = BufReader::new(File::open(path).unwrap());
        let (vbuf, slice) = model::load_c3d(&mut file, factory);
        let pso = render::Render::create_object_pso(factory);
        let data = render::object::Data {
            vbuf: vbuf,
            locals: factory.create_constant_buffer(1),
            palette: render::Render::create_palette(&pal_data, factory),
            out: output,
        };
        ObjectViewApp {
            bundle: gfx::Bundle::new(slice, pso, data),
            cam: Camera {
                loc: cgmath::vec3(0.0, -100.0, 50.0),
                rot: cgmath::Quaternion::new(0.0, 1.0, 0.0, 0.0),
                proj: cgmath::PerspectiveFov {
                    fovy: cgmath::deg(45.0).into(),
                    aspect: settings.get_screen_aspect(),
                    near: 1.0,
                    far: 300.0,
                },
            },
        }
    }
}

fn main() {
    use std::env;
    env_logger::init().unwrap();

    info!("Loading the settings");
    let settings = config::Settings::load("config/settings.toml");

    info!("Creating the window with GL context");
    let builder = glutin::WindowBuilder::new()
        .with_title(settings.window.title.clone())
        .with_dimensions(settings.window.size[0], settings.window.size[1])
        .with_vsync();
    let (window, mut device, mut factory, main_color, _main_depth) =
        gfx_window_glutin::init::<render::ColorFormat, render::DepthFormat>(builder);

    info!("Parsing command line");
    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();
    let mut options = getopts::Options::new();
    options
        .parsing_style(getopts::ParsingStyle::StopAtFirstFree)
        .optflag("h", "help", "print this help menu")
        .optopt("v", "view", "view a particular game resource", "");
    let matches = options.parse(&args[1..]).unwrap();
    if matches.opt_present("h") || !matches.free.is_empty() {
        let brief = format!("Usage: {} [options]", program);
        println!("{}", options.usage(&brief));
        return;
    }

    //let app = match matches.opt_str("v") {}
    let mut app = Box::new(GameApp::new(&settings, main_color, &mut factory));

    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    'main: loop {
        use gfx::Device;
        for event in window.poll_events() {
            if !app.on_event(event, &mut factory) {
                break 'main;
            }
        }
        app.on_frame(&mut encoder);
        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}
<commit_msg>Implemented App for the object view mode<commit_after>extern crate byteorder;
extern crate cgmath;
extern crate env_logger;
extern crate getopts;
#[macro_use]
extern crate gfx;
extern crate gfx_window_glutin;
extern crate glutin;
#[macro_use]
extern crate log;
extern crate progressive;
extern crate rustc_serialize;
extern crate ini;
extern crate toml;

mod config;
mod level;
mod model;
mod render;
mod splay;

use glutin::Event;

pub struct Camera {
    loc: cgmath::Vector3<f32>,
    rot: cgmath::Quaternion<f32>,
    proj: cgmath::PerspectiveFov<f32>,
}

impl Camera {
    pub fn get_view_proj(&self) -> cgmath::Matrix4<f32> {
        use cgmath::{Decomposed, Matrix4, Transform};
        let view = Decomposed {
            scale: 1.0,
            rot: self.rot,
            disp: self.loc,
        };
        let view_mx: Matrix4<f32> = view.inverse_transform().unwrap().into();
        let proj_mx: Matrix4<f32> = self.proj.into();
        proj_mx * view_mx
    }
}

trait App<R: gfx::Resources> {
    fn on_event<F: gfx::Factory<R>>(&mut self, Event, &mut F) -> bool;
    fn on_frame<C: gfx::CommandBuffer<R>>(&mut self, &mut gfx::Encoder<R, C>);
}

struct GameApp<R: gfx::Resources> {
    render: render::Render<R>,
    cam: Camera,
}

impl<R: gfx::Resources> GameApp<R> {
    fn new<F: gfx::Factory<R>>(settings: &config::Settings,
           output: gfx::handle::RenderTargetView<R, render::ColorFormat>,
           factory: &mut F) -> GameApp<R>
    {
        info!("Loading world parameters");
        let config = settings.get_level();
        let lev = level::load(&config);

        GameApp {
            render: render::init(factory, output, &lev),
            cam: Camera {
                loc: cgmath::vec3(0.0, 0.0, 200.0),
                rot: cgmath::Quaternion::new(1.0, 0.0, 0.0, 0.0),
                proj: cgmath::PerspectiveFov {
                    fovy: cgmath::deg(45.0).into(),
                    aspect: settings.get_screen_aspect(),
                    near: 1.0,
                    far: 100000.0,
                },
            },
        }
    }
}

impl<R: gfx::Resources> App<R> for GameApp<R> {
    fn on_event<F: gfx::Factory<R>>(&mut self, event: Event, factory: &mut F) -> bool {
        use cgmath::Rotation3;
        use glutin::VirtualKeyCode as Key;
        let delta = cgmath::rad(0.05);
        let step = 10.0;
        match event {
            Event::KeyboardInput(_, _, Some(Key::Escape)) |
            Event::Closed => return false,
            Event::KeyboardInput(_, _, Some(Key::L)) => self.render.reload(factory),
            Event::KeyboardInput(_, _, Some(Key::W)) =>
                self.cam.loc = self.cam.loc + cgmath::vec3(0.0, step, 0.0),
            Event::KeyboardInput(_, _, Some(Key::S)) =>
                self.cam.loc = self.cam.loc - cgmath::vec3(0.0, step, 0.0),
            Event::KeyboardInput(_, _, Some(Key::R)) =>
                self.cam.rot = self.cam.rot * cgmath::Quaternion::from_axis_angle(cgmath::vec3(1.0, 0.0, 0.0), delta),
            Event::KeyboardInput(_, _, Some(Key::F)) =>
                self.cam.rot = self.cam.rot * cgmath::Quaternion::from_axis_angle(cgmath::vec3(1.0, 0.0, 0.0), -delta),
            Event::KeyboardInput(_, _, Some(Key::A)) =>
                self.cam.rot = cgmath::Quaternion::from_axis_angle(cgmath::vec3(0.0, 0.0, 1.0), delta) * self.cam.rot,
            Event::KeyboardInput(_, _, Some(Key::D)) =>
                self.cam.rot = cgmath::Quaternion::from_axis_angle(cgmath::vec3(0.0, 0.0, 1.0), -delta) * self.cam.rot,
            _ => {},
        }
        true
    }
    fn on_frame<C: gfx::CommandBuffer<R>>(&mut self, enc: &mut gfx::Encoder<R, C>) {
        self.render.draw(enc, &self.cam);
    }
}

struct ObjectViewApp<R: gfx::Resources> {
    bundle: gfx::Bundle<R, render::object::Data<R>>,
    cam: Camera,
}

impl<R: gfx::Resources> ObjectViewApp<R> {
    fn new<F: gfx::Factory<R>>(path: &str, settings: &config::Settings,
           output: gfx::handle::RenderTargetView<R, render::ColorFormat>,
           factory: &mut F) -> ObjectViewApp<R>
    {
        use std::io::BufReader;
        use std::fs::File;
        use gfx::traits::FactoryExt;

        let pal_data = level::load_palette(&settings.get_object_palette_path());

        let mut file = BufReader::new(File::open(path).unwrap());
        let (vbuf, slice) = model::load_c3d(&mut file, factory);
        let pso = render::Render::create_object_pso(factory);
        let data = render::object::Data {
            vbuf: vbuf,
            locals: factory.create_constant_buffer(1),
            palette: render::Render::create_palette(&pal_data, factory),
            out: output,
        };
        ObjectViewApp {
            bundle: gfx::Bundle::new(slice, pso, data),
            cam: Camera {
                loc: cgmath::vec3(0.0, -100.0, 50.0),
                rot: cgmath::Quaternion::new(0.0, 1.0, 0.0, 0.0),
                proj: cgmath::PerspectiveFov {
                    fovy: cgmath::deg(45.0).into(),
                    aspect: settings.get_screen_aspect(),
                    near: 1.0,
                    far: 300.0,
                },
            },
        }
    }
}

impl<R: gfx::Resources> App<R> for ObjectViewApp<R> {
    fn on_event<F: gfx::Factory<R>>(&mut self, event: Event, factory: &mut F) -> bool {
        use glutin::VirtualKeyCode as Key;
        match event {
            Event::KeyboardInput(_, _, Some(Key::Escape)) |
            Event::Closed => return false,
            _ => {}, //TODO
        }
        true
    }
    fn on_frame<C: gfx::CommandBuffer<R>>(&mut self, enc: &mut gfx::Encoder<R, C>) {
        enc.clear(&self.bundle.data.out, [0.1, 0.2, 0.3, 1.0]);
        self.bundle.encode(enc);
    }

}

fn main() {
    use std::env;
    env_logger::init().unwrap();

    info!("Loading the settings");
    let settings = config::Settings::load("config/settings.toml");

    info!("Creating the window with GL context");
    let builder = glutin::WindowBuilder::new()
        .with_title(settings.window.title.clone())
        .with_dimensions(settings.window.size[0], settings.window.size[1])
        .with_vsync();
    let (window, mut device, mut factory, main_color, _main_depth) =
        gfx_window_glutin::init::<render::ColorFormat, render::DepthFormat>(builder);

    info!("Parsing command line");
    let args: Vec<_> = env::args().collect();
    let program = args[0].clone();
    let mut options = getopts::Options::new();
    options
        .parsing_style(getopts::ParsingStyle::StopAtFirstFree)
        .optflag("h", "help", "print this help menu")
        .optopt("v", "view", "view a particular game resource", "");
    let matches = options.parse(&args[1..]).unwrap();
    if matches.opt_present("h") || !matches.free.is_empty() {
        let brief = format!("Usage: {} [options]", program);
        println!("{}", options.usage(&brief));
        return;
    }

    let mut app = match matches.opt_str("v") {
        //Some(path) => Box::new(ObjectViewApp::new(&path, &settings, main_color, &mut factory)),
        _ => Box::new(GameApp::new(&settings, main_color, &mut factory)),
    };

    let mut encoder: gfx::Encoder<_, _> = factory.create_command_buffer().into();
    'main: loop {
        use gfx::Device;
        for event in window.poll_events() {
            if !app.on_event(event, &mut factory) {
                break 'main;
            }
        }
        app.on_frame(&mut encoder);
        encoder.flush(&mut device);
        window.swap_buffers().unwrap();
        device.cleanup();
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod error;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;

use docopt::Docopt;
use commit_analyzer::CommitType;
use std::process;
use semver::Version;
use std::env;
use std::error::Error;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w, --write            Run with writing the changes afterwards.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: bool,
    flag_version: bool,
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let is_dry_run = if ci_env_set() {
        false
    }
    else {
        !args.flag_write
    };

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let repository_path = &args.flag_path;

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    let _signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
        Err(e) => {
            logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
            logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.
Make sure to specify it in one of the following locations:

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config
                           ");
            process::exit(1);
        }
    };

    let version = match toml_file::read_from_file(repository_path) {
        Ok(toml) => toml,
        Err(e) => {
            logger::stderr(format!("Reading `Cargo.toml` failed: {:?}", e));
            process::exit(1);
        }
    };

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(repository_path);
    if is_dry_run {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    else {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if is_dry_run {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    }
    else {
        logger::stdout(format!("New version: {}", new_version));

        match toml_file::write_new_version(repository_path, &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Writing `Cargo.toml` failed: {:?}", err));
                process::exit(1);
            }
        }

        logger::stdout(format!("Writing Changelog"));
        match changelog::write(repository_path, &version.to_string(), &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Writing Changelog failed: {:?}", err));
                process::exit(1);
            }
        }

        match git::commit_files(repository_path, &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Committing `Cargo.toml` and `Changelog.md` failed: {:?}", err));
                process::exit(1);
            }
        }

        logger::stdout("Creating annotated git tag");
        let tag_message = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(msg) => msg,
            Err(err) => {
                logger::stderr(format!("Can't generate changelog: {:?}", err));
                process::exit(1);
            }
        };

        let tag_name = format!("v{}", new_version);
        match git::tag(repository_path, &tag_name, &tag_message) {
            Ok(_) => { },
            Err(err) => {
                logger::stderr(format!("Failed to create git tag: {:?}", err));
                process::exit(1);
            }
        }
    }
}
<commit_msg>style: Remove useless whitepsace and superfluous comment<commit_after>#![cfg_attr(feature = "dev", allow(unstable_features))]
#![cfg_attr(feature = "dev", feature(plugin))]
#![cfg_attr(feature = "dev", plugin(clippy))]

mod logger;
mod toml_file;
mod git;
mod changelog;
mod commit_analyzer;
mod error;

extern crate rustc_serialize;
extern crate toml;
extern crate regex;
extern crate semver;
extern crate docopt;
extern crate git2;
extern crate clog;

use docopt::Docopt;
use commit_analyzer::CommitType;
use std::process;
use semver::Version;
use std::env;
use std::error::Error;

const VERSION: &'static str = env!("CARGO_PKG_VERSION");
const USAGE: &'static str = "
semantic.rs 🚀

Usage:
  semantic-rs [options]
  semantic-rs --version

Options:
  -h --help              Show this screen.
  --version              Show version.
  -p PATH, --path=PATH   Specifies the repository path. [default: .]
  -w, --write            Run with writing the changes afterwards.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    flag_path: String,
    flag_write: bool,
    flag_version: bool,
}

fn version_bump(version: &Version, bump: CommitType) -> Option<Version> {
    let mut version = version.clone();
    match bump {
        CommitType::Unknown => return None,
        CommitType::Patch => version.increment_patch(),
        CommitType::Minor => version.increment_minor(),
        CommitType::Major => version.increment_major(),
    }

    Some(version)
}

fn ci_env_set() -> bool {
    env::var("CI").is_ok()
}

fn main() {
    let args: Args = Docopt::new(USAGE)
        .and_then(|d| d.decode())
        .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("semantic.rs 🚀 -- v{}", VERSION);
        process::exit(0);
    }

    let is_dry_run = if ci_env_set() {
        false
    }
    else {
        !args.flag_write
    };

    println!("semantic.rs 🚀");

    logger::stdout("Analyzing your repository");
    let repository_path = &args.flag_path;

    let repo = match git2::Repository::open(repository_path) {
        Ok(repo) => repo,
        Err(e) => {
            logger::stderr(format!("Could not open the git repository: {:?}", e));
            process::exit(1);
        }
    };

    let _signature = match git::get_signature(&repo) {
        Ok(sig) => sig,
        Err(e) => {
            logger::stderr(format!("Failed to get the committer's name and email address: {}", e.description()));
            logger::stderr(r"
A release commit needs a committer name and email address.
We tried fetching it from different locations, but couldn't find one.

Committer information is taken from the following environment variables, if set:

GIT_COMMITTER_NAME
GIT_COMMITTER_EMAIL

If none is set the normal git config is tried in the following order:

Local repository config
User config
Global config");
            process::exit(1);
        }
    };

    let version = match toml_file::read_from_file(repository_path) {
        Ok(toml) => toml,
        Err(e) => {
            logger::stderr(format!("Reading `Cargo.toml` failed: {:?}", e));
            process::exit(1);
        }
    };

    let version = Version::parse(&version).expect("Not a valid version");
    logger::stdout(format!("Current version: {}", version.to_string()));

    logger::stdout("Analyzing commits");

    let bump = git::version_bump_since_latest(repository_path);
    if is_dry_run {
        logger::stdout(format!("Commits analyzed. Bump would be {:?}", bump));
    }
    else {
        logger::stdout(format!("Commits analyzed. Bump will be {:?}", bump));
    }
    let new_version = match version_bump(&version, bump) {
        Some(new_version) => new_version.to_string(),
        None => {
            logger::stdout("No version bump. Nothing to do.");
            process::exit(0);
        }
    };

    if is_dry_run {
        logger::stdout(format!("New version would be: {}", new_version));
        logger::stdout("Would write the following Changelog:");
        let changelog = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(log) => log,
            Err(err) => {
                logger::stderr(format!("Generating Changelog failed: {:?}", err));
                process::exit(1);
            }
        };
        logger::stdout("====================================");
        logger::stdout(changelog);
        logger::stdout("====================================");
        logger::stdout("Would create annotated git tag");
    }
    else {
        logger::stdout(format!("New version: {}", new_version));

        match toml_file::write_new_version(repository_path, &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Writing `Cargo.toml` failed: {:?}", err));
                process::exit(1);
            }
        }

        logger::stdout(format!("Writing Changelog"));
        match changelog::write(repository_path, &version.to_string(), &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Writing Changelog failed: {:?}", err));
                process::exit(1);
            }
        }

        match git::commit_files(repository_path, &new_version) {
            Ok(_)    => { },
            Err(err) => {
                logger::stderr(format!("Committing `Cargo.toml` and `Changelog.md` failed: {:?}", err));
                process::exit(1);
            }
        }

        logger::stdout("Creating annotated git tag");
        let tag_message = match changelog::generate(repository_path, &version.to_string(), &new_version) {
            Ok(msg) => msg,
            Err(err) => {
                logger::stderr(format!("Can't generate changelog: {:?}", err));
                process::exit(1);
            }
        };

        let tag_name = format!("v{}", new_version);
        match git::tag(repository_path, &tag_name, &tag_message) {
            Ok(_) => { },
            Err(err) => {
                logger::stderr(format!("Failed to create git tag: {:?}", err));
                process::exit(1);
            }
        }
    }
}
<|endoftext|>


<commit_before>use png;
use color::{Color, Black, White, Red, Green, Blue};
use point::Point;
use scene::{Scene, Spot};
use shapes::{Object, Sphere, Plane};

static WIDTH: int = 800;
static HEIGHT: int = 800;

#[main]
fn main() {
  let mut pixels = [Black, ..((WIDTH * HEIGHT) as uint)];

  let scene = Scene {
    eye: Point::new(-1200., 0., 0.),
    spots: ~[
      Spot {
        pos: Point::new(-1000., -100., 100.),
        color: Blue
      },
      Spot {
        pos: Point::new(-1000., 100., 100.),
        color: Green
      },
    ],
    objects: ~[
      Object {
        pos: Point::new(0., 0., 0.),
        shininess: 0.8,
        color: Black,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., 0., -100.),
        shininess: 0.8,
        color: White,
        shape: ~Plane
      },
    ]
  };

  for x in range(0, WIDTH) {
    for y in range(0, HEIGHT) {
      let vector = Point {
        x: 1000.,
        y: (WIDTH / 2 - x) as f64,
        z: (HEIGHT / 2 - y) as f64
      };

      let (obj, k) = scene.get_closest(&vector);

      pixels[y * WIDTH + x] =
        if obj.is_none() || k <= 0. {
           Black
        }
        else {
          let closest = obj.unwrap();
          scene.get_color(closest, &vector, k)
        };
    }
  }

  let img = png::Image {
    width: WIDTH as u32,
    height: HEIGHT as u32,
    color_type: png::RGB8,
    pixels: pixels.flat_map(|&Color { r, g, b }| { ~[r, g, b] })
  };

  png::store_png(&img, &Path::new("out.png"));
}<commit_msg>Fix fisheye effect<commit_after>use png;
use color::{Color, Black, White, Red, Green, Blue};
use point::Point;
use scene::{Scene, Spot};
use shapes::{Object, Sphere, Plane};

static WIDTH: int = 1000;
static HEIGHT: int = 1000;

#[main]
fn main() {
  let mut pixels = ~[Black, ..((WIDTH * HEIGHT) as uint)];

  let scene = Scene {
    eye: Point::new(-12000., 0., 0.),
    spots: ~[
      Spot {
        pos: Point::new(-1000., -200., 0.),
        color: White
      },
    ],
    objects: ~[
      Object {
        pos: Point::new(0., 400., 400.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., 400., 0.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., 400., -400.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., -400., 400.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., -400., 0.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., -400., -400.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., 0., 400.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., 0., 0.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
      Object {
        pos: Point::new(0., 0., -400.),
        shininess: 0.,
        color: Green,
        shape: ~Sphere { radius: 100. }
      },
    ]
  };

  for x in range(0, WIDTH) {
    for y in range(0, HEIGHT) {
      let vector = Point {
        x: 10000.,
        y: (WIDTH / 2 - x) as f64,
        z: (HEIGHT / 2 - y) as f64
      };

      let (obj, k) = scene.get_closest(&vector);

      pixels[y * WIDTH + x] =
        if obj.is_none() || k <= 0. {
           Black
        }
        else {
          let closest = obj.unwrap();
          scene.get_color(closest, &vector, k)
        };
    }
  }

  let img = png::Image {
    width: WIDTH as u32,
    height: HEIGHT as u32,
    color_type: png::RGB8,
    pixels: pixels.flat_map(|&Color { r, g, b }| { ~[r, g, b] })
  };

  png::store_png(&img, &Path::new("out.png"));
}<|endoftext|>


<commit_before>
#[macro_use]
extern crate clap;
extern crate regex;
extern crate chrono;
extern crate lettre;
extern crate colored;
extern crate notify_rust;

use clap::App;
use colored::*;
use regex::Regex;
use chrono::prelude::*;
use std::{thread, time};
use notify_rust::Notification;
use lettre::email::EmailBuilder;
use lettre::transport::EmailTransport;
use lettre::transport::smtp::{SecurityLevel, SmtpTransportBuilder};

use std::process::exit;

fn main() {
    let re = Regex::new(r"([01]?\d):(\d{2})").unwrap();
    let yml = load_yaml!("cli.yml");
    let app = App::from_yaml(yml).version(crate_version!()).get_matches();
    match app.subcommand() {
        ("run", Some(content)) => {
            let time = content.value_of("time").unwrap();
            if re.is_match(time) {
                println!("{} Run with notifications", "[Notice]".yellow().bold());
                let captures = re.captures(time).unwrap();
                execute(None,
                        captures.get(1).unwrap().as_str(),
                        captures.get(2).unwrap().as_str());
            } else {
                exit(1);
            }
        }
        ("server", Some(sub_m)) => {
            let time = sub_m.value_of("time").unwrap();
            if re.is_match(time) {
                println!("{} Run as server", "[Notice]".yellow().bold());
                let captures = re.captures(time).unwrap();
                execute(Some(sub_m),
                        captures.get(1).unwrap().as_str(),
                        captures.get(2).unwrap().as_str())
            } else {
                exit(1);
            }
        }
        _ => println!("{}", app.usage()),
    }
}

fn execute(server_info: Option<&clap::ArgMatches>, hour: &str, minute: &str) {
    let notification = match server_info {
        Some(_) => false,
        None => true,
    };
    let mut sleep_duration;
    let delta = time_delta(hour, minute, true);
    if delta.as_secs() > 0 {
        let now = Local::now();
        println!("{} {}\tFirst notification will be delivered in {} seconds",
                 "[Notice]".yellow().bold(),
                 now.format("%Y/%m/%d %H:%M:%S").to_string(),
                 delta.as_secs());
        thread::sleep(delta);
    }
    loop {
        sleep_duration = time_delta(hour, minute, false);
        if notification {
            deliver_notifiaction();
        } else {
            send_mail(server_info.unwrap());
        }
        thread::sleep(sleep_duration);
    }
}

fn send_mail(args: &clap::ArgMatches) {
    let now = Local::now();
    let mail = EmailBuilder::new()
        .to(args.value_of("mail").unwrap())
        .from((args.value_of("mail").unwrap(), "Once a day"))
        .body("Be friendly and make a compliment today! :-)")
        .subject("Your compliment today")
        .build()
        .unwrap();
    let port = match args.value_of("port") {
        Some(x) => x.parse::<u16>().unwrap(),
        None => 587 as u16,
    };
    let mut sender = SmtpTransportBuilder::new((args.value_of("host").unwrap(), port))
        .unwrap()
        .credentials(args.value_of("user").unwrap(),
                     args.value_of("pass").unwrap())
        .security_level(SecurityLevel::AlwaysEncrypt)
        .smtp_utf8(true)
        .connection_reuse(true)
        .build();
    match sender.send(mail) {
        Ok(_) => {
            println!("{} {}\tMail send to {}",
                     "[Log]   ".green().bold(),
                     now.format("%Y/%m/%d %H:%M:%S").to_string(),
                     args.value_of("mail").unwrap())
        }
        Err(x) => {
            println!("{} {}\tMail could not be sent because of {}",
                     "[Error]".red().bold(),
                     now.format("%Y/%m/%d %H:%M:%S").to_string(),
                     x)
        }
    }
}

fn time_delta(hour: &str, minute: &str, first: bool) -> time::Duration {
    let trigger_base = if first {
        Local::today()
    } else {
        match Local::today().succ_opt() {
            Some(date) => date,
            None => {
                let now = Local::now();
                println!("{} {}\tThere is no tomorrow",
                         "[Error]".red().bold(),
                         now.format("%Y/%m/%d %H:%M:%S").to_string());
                exit(1);
            }
        }
    };
    let trigger_time = trigger_base.and_hms(hour.parse::<u32>().unwrap(),
                                            minute.parse::<u32>().unwrap(),
                                            0);
    let time_delta = trigger_time.timestamp() - Local::now().timestamp();
    time::Duration::from_secs(time_delta as u64)
}

#[cfg(all(unix))]
fn deliver_notifiaction() {
    let now = Local::now();
    match Notification::new()
        .summary("Once a day")
        .body("Be friendly and make a compliment today! 😊")
        .show() {
        Ok(_) => {
            println!("{} {}\tNotification delivered",
                     "[Log]   ".green().bold(),
                     now.format("%Y/%m/%d %H:%M:%S").to_string())
        }
        Err(x) => {
            println!("{} {}\tNotification could not be delivered because of {:?}",
                     "[Error]".red().bold(),
                     now.format("%Y/%m/%d %H:%M:%S").to_string(),
                     x)
        }
    }

}

#[cfg(windows)]
fn deliver_notifiaction(now: &date::Date) {
    let now = Local::now();
    println!("{} {}\tNo notification support for Windows right now.",
             "[Error]".red().bold(),
             now.format("%Y/%m/%d %H:%M:%S").to_string());
    exit(1);
}
<commit_msg>fixing negative time_delta<commit_after>
#[macro_use]
extern crate clap;
extern crate regex;
extern crate chrono;
extern crate lettre;
extern crate colored;
extern crate notify_rust;

use clap::App;
use colored::*;
use regex::Regex;
use chrono::prelude::*;
use std::{thread, time};
use notify_rust::Notification;
use lettre::email::EmailBuilder;
use lettre::transport::EmailTransport;
use lettre::transport::smtp::{SecurityLevel, SmtpTransportBuilder};

use std::process::exit;

fn main() {
    let re = Regex::new(r"([01]?\d):(\d{2})").unwrap();
    let yml = load_yaml!("cli.yml");
    let app = App::from_yaml(yml).version(crate_version!()).get_matches();
    match app.subcommand() {
        ("run", Some(content)) => {
            let time = content.value_of("time").unwrap();
            if re.is_match(time) {
                println!("{} Run with notifications", "[Notice]".yellow().bold());
                let captures = re.captures(time).unwrap();
                execute(None,
                        captures.get(1).unwrap().as_str(),
                        captures.get(2).unwrap().as_str());
            } else {
                exit(1);
            }
        }
        ("server", Some(sub_m)) => {
            let time = sub_m.value_of("time").unwrap();
            if re.is_match(time) {
                println!("{} Run as server", "[Notice]".yellow().bold());
                let captures = re.captures(time).unwrap();
                execute(Some(sub_m),
                        captures.get(1).unwrap().as_str(),
                        captures.get(2).unwrap().as_str())
            } else {
                exit(1);
            }
        }
        _ => println!("{}", app.usage()),
    }
}

fn execute(server_info: Option<&clap::ArgMatches>, hour: &str, minute: &str) {
    let notification = match server_info {
        Some(_) => false,
        None => true,
    };
    let mut sleep_duration;
    let delta = time_delta(hour, minute, true);
    if delta.as_secs() > 0 {
        let now = Local::now();
        println!("{} {}\tFirst notification will be delivered in {} seconds",
                 "[Notice]".yellow().bold(),
                 now.format("%Y/%m/%d %H:%M:%S").to_string(),
                 delta.as_secs());
        thread::sleep(delta);
    }
    loop {
        sleep_duration = time_delta(hour, minute, false);
        if notification {
            deliver_notifiaction();
        } else {
            send_mail(server_info.unwrap());
        }
        thread::sleep(sleep_duration);
    }
}

fn send_mail(args: &clap::ArgMatches) {
    let now = Local::now();
    let mail = EmailBuilder::new()
        .to(args.value_of("mail").unwrap())
        .from((args.value_of("mail").unwrap(), "Once a day"))
        .body("Be friendly and make a compliment today! :-)")
        .subject("Your compliment today")
        .build()
        .unwrap();
    let port = match args.value_of("port") {
        Some(x) => x.parse::<u16>().unwrap(),
        None => 587 as u16,
    };
    let mut sender = SmtpTransportBuilder::new((args.value_of("host").unwrap(), port))
        .unwrap()
        .credentials(args.value_of("user").unwrap(),
                     args.value_of("pass").unwrap())
        .security_level(SecurityLevel::AlwaysEncrypt)
        .smtp_utf8(true)
        .connection_reuse(true)
        .build();
    match sender.send(mail) {
        Ok(_) => {
            println!("{} {}\tMail send to {}",
                     "[Log]   ".green().bold(),
                     now.format("%Y/%m/%d %H:%M:%S").to_string(),
                     args.value_of("mail").unwrap())
        }
        Err(x) => {
            println!("{} {}\tMail could not be sent because of {}",
                     "[Error]".red().bold(),
                     now.format("%Y/%m/%d %H:%M:%S").to_string(),
                     x)
        }
    }
}

fn time_delta(hour: &str, minute: &str, first: bool) -> time::Duration {
    let trigger_base = if first {
        Local::today()
    } else {
        match Local::today().succ_opt() {
            Some(date) => date,
            None => {
                let now = Local::now();
                println!("{} {}\tThere is no tomorrow",
                         "[Error]".red().bold(),
                         now.format("%Y/%m/%d %H:%M:%S").to_string());
                exit(1);
            }
        }
    };
    let trigger_time = trigger_base.and_hms(hour.parse::<u32>().unwrap(),
                                            minute.parse::<u32>().unwrap(),
                                            0);
    let time_delta = trigger_time.timestamp() - Local::now().timestamp();
    if time_delta <= 0 {
        time::Duration::from_secs(0)
    } else {
        time::Duration::from_secs(time_delta as u64)
    }
}

#[cfg(all(unix))]
fn deliver_notifiaction() {
    let now = Local::now();
    match Notification::new()
        .summary("Once a day")
        .body("Be friendly and make a compliment today! 😊")
        .show() {
        Ok(_) => {
            println!("{} {}\tNotification delivered",
                     "[Log]   ".green().bold(),
                     now.format("%Y/%m/%d %H:%M:%S").to_string())
        }
        Err(x) => {
            println!("{} {}\tNotification could not be delivered because of {:?}",
                     "[Error]".red().bold(),
                     now.format("%Y/%m/%d %H:%M:%S").to_string(),
                     x)
        }
    }

}

#[cfg(windows)]
fn deliver_notifiaction(now: &date::Date) {
    let now = Local::now();
    println!("{} {}\tNo notification support for Windows right now.",
             "[Error]".red().bold(),
             now.format("%Y/%m/%d %H:%M:%S").to_string());
    exit(1);
}
<|endoftext|>


<commit_before>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *   [
  *     [0,11723767],
  *     [1, 65802]
  *    ]
  *  ],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
  * the function names for the corresponding addresses (in the "stacks" array) and returns JSON
  * such as:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

extern crate breakpad_symbols;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use hyper::Client;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/**
  * Incoming JSON request format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // index, offset
    stacks: Vec<Vec<(i8,u64)>>,
    version: u8,
}

/**
  * Outgoing JSON response format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    symbolicatedStacks: Vec<Vec<String>>,
    knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let address = "0.0.0.0:5000";

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();

            debug!("decoded.stacks: {:?}", decoded.stacks);

            let stacks = decoded.stacks[0].clone();
            for stack in &stacks {
                let (index, offset) = *stack;

                let mut offsets = vec!();
                if stack_map.contains_key(&index) {
                    offsets = stack_map.get(&index).unwrap().clone();
                }
                offsets.push(offset);
                stack_map.insert(index, offsets);
            }

            debug!("stack_map: {:?}", stack_map);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map.clone());
            res.write_all(symbol_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = 0;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let mut body = String::new();
                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                res.read_to_string(&mut body).unwrap();

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);
            // FIXME this is having problems with CRLF

            let mut symbols = vec!();
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    debug!("attempt to symbolicate: {} for: {:?}", *address, &symbol_path);
                    match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                        Some(x) => symbols.push(x),
                        // return the address rather than function name if symbol not found
                        None => symbols.push(format!("0x{:x}", address)),
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    for handle in handles {
        let (debug_file_name, symbols) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(true);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<commit_msg>handle -1<commit_after>// Copyright 2015 Robert Helmer <rhelmer@rhelmer.org>. See the LICENSE
// file at the top-level directory of this distribution.

/**
  * Symbolapi - a microservice which accepts lists of symbol+addresses, and returns
  * a list of symbolicated functions.
  *
  * The goal is to take an HTTP JSON request such as:
  * {"stacks":[
  *   [
  *     [0,11723767],
  *     [1, 65802]
  *    ]
  *  ],
  *  "memoryMap":[
  *    ["xul.pdb","44E4EC8C2F41492B9369D6B9A059577C2"],
  *    ["wntdll.pdb","D74F79EB1F8D4A45ABCD2F476CCABACC2"]
  *  ],
  *  "version":4
  * }
  *
  * The symbolapi service then downloads the corresponding symbol files (e.g. from S3), and returns
  * the function names for the corresponding addresses (in the "stacks" array) and returns JSON
  * such as:
  * {"symbolicatedStacks": [
  *   [
  *     "XREMain::XRE_mainRun() (in xul.pdb)",
  *     "KiUserCallbackDispatcher (in wntdll.pdb)"]
  *   ],
  *   "knownModules": [true, true]
  * }
  */

extern crate breakpad_symbols;
extern crate hyper;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate rustc_serialize;
extern crate toml;

use std::collections::HashMap;
use std::fs::{File, create_dir_all};
use std::io::{Read, Write};
use std::path::{PathBuf};
use std::thread;

use breakpad_symbols::{Symbolizer, SimpleSymbolSupplier};
use hyper::Client;
use hyper::server::{Server, Request, Response};
use hyper::status::StatusCode;
use rustc_serialize::json;

/**
  * Incoming JSON request format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcDecodable)]
pub struct SymbolRequest {
    memoryMap: Vec<(String,String)>,
    // index, offset
    stacks: Vec<Vec<(i8,u64)>>,
    version: u8,
}

/**
  * Outgoing JSON response format.
  */
// required JSON keys are non-snakecase
#[allow(non_snake_case)]
#[derive(RustcEncodable)]
pub struct SymbolResponse {
    symbolicatedStacks: Vec<Vec<String>>,
    knownModules: Vec<bool>,
}

fn main() {
    log4rs::init_file("config/log.toml", Default::default()).unwrap();

    let address = "0.0.0.0:5000";

    info!("Listening on {}", address);
    Server::http(address).unwrap().handle(server).unwrap();
}

/**
  * Receives single HTTP requests and demuxes to symbols file fetches from S3 bucket.
  */
fn server(mut req: Request, mut res: Response) {
    info!("incoming connection from {}", req.remote_addr);

    match req.method {
        hyper::Post => {
            let mut res = res.start().unwrap();

            let mut buffer = String::new();
            req.read_to_string(&mut buffer).unwrap();
            debug!("raw POST: {:?}", &buffer);

            let decoded: SymbolRequest = json::decode(&buffer).unwrap();

            debug!("decoded memoryMap: {:?}", decoded.memoryMap);
            debug!("decoded stacks: {:?}", decoded.stacks);
            debug!("decoded version: {}", decoded.version);

            let symbol_url = get_config("symbol_urls.public");

            // stacks come in as an array, turn into hashmap
            let mut stack_map: HashMap<i8, Vec<u64>> = HashMap::new();

            debug!("decoded.stacks: {:?}", decoded.stacks);

            let stacks = decoded.stacks[0].clone();
            for stack in &stacks {
                let (index, offset) = *stack;

                let mut offsets = vec!();
                if stack_map.contains_key(&index) {
                    offsets = stack_map.get(&index).unwrap().clone();
                }
                offsets.push(offset);
                stack_map.insert(index, offsets);
            }

            debug!("stack_map: {:?}", stack_map);

            // FIXME limit the number of possible threads
            // TODO maybe push these into a queue and have a thread pool service the queue?
            let symbol_response = client(symbol_url, decoded.memoryMap, stack_map.clone());
            res.write_all(symbol_response.as_bytes()).unwrap();

            res.end().unwrap();
        },
        hyper::Get => {
            let mut res = res.start().unwrap();
            res.write_all("symbolapi, see github.com/rhelmer/symbolapi".as_bytes()).unwrap();
        },
        _ => { *res.status_mut() = StatusCode::MethodNotAllowed },
    }

    debug!("finished serving request");
}

/**
  * Creates multiple client connections and aggregates result.
  */
fn client(url: String, memory_map: Vec<(String,String)>, stack_map: HashMap<i8, Vec<u64>>) -> String {
    let mut handles = vec![];
    let mut counter: i8 = -1;
    for (debug_file, debug_id) in memory_map {
        let debug_file_name = debug_file.clone();
        let pdb = debug_file.find(".pdb").unwrap();
        let (symbol_name, _) = debug_file.split_at(pdb);
        let symbol_file = format!("{}.sym", symbol_name);
        let this_url = format!("{}/{}/{}/{}", url, debug_file, debug_id, symbol_file);

        let mut symbol_path = PathBuf::new();
        // TODO get from config
        symbol_path.push("testdata/symbols");

        let mut full_symbol_path = symbol_path.clone();
        full_symbol_path.push(&debug_file);
        full_symbol_path.push(&debug_id);
        full_symbol_path.push(&symbol_file);

        let stack_map_copy = stack_map.clone();

        create_dir_all(&full_symbol_path.parent().unwrap()).unwrap();

        let supplier = SimpleSymbolSupplier::new(vec!(symbol_path.clone()));

        // TODO most of the time is probably spent waiting on I/O, maybe async would be more appropriate?
        // TODO decide min/max possible threads, possibly based on number of cores?
        handles.push(thread::spawn(move || {

            let symbolizer = Symbolizer::new(supplier);
            // FIXME use Arc<Mutex<File>> to prevent concurrent writes
            // TODO only write contents if server version newer
            if !&full_symbol_path.exists() {

                let mut body = String::new();
                let c = Client::new();
                let mut res = c.get(&this_url).send().unwrap();
                res.read_to_string(&mut body).unwrap();

                let mut f = File::create(&full_symbol_path).unwrap();
                f.write_all(body.as_bytes()).unwrap();
            }

            debug!("symbol_path: {:?}", &symbol_path);
            // FIXME this is having problems with CRLF

            let mut symbols = vec!();
            for stacks in stack_map_copy.get(&counter) {
                for address in stacks {
                    if counter == -1 {
                        symbols.push(format!("{}", address));
                    } else {
                        debug!("attempt to symbolicate: {} for: {:?}", *address, &symbol_path);
                        match symbolizer.get_symbol_at_address(&debug_file_name, &debug_id, *address) {
                            Some(x) => symbols.push(x),
                            // return the address rather than function name if symbol not found
                            None => symbols.push(format!("0x{:x}", address)),
                        }
                    }
                }
            }
            debug!("debug_file_name: {:?}, symbols: {:?}", debug_file_name, symbols);

            (debug_file_name, symbols)
        }));

        counter += 1;
    }

    let mut result = SymbolResponse {
        symbolicatedStacks: vec!(),
        knownModules: vec!(),
    };

    let mut symbolicated_stacks = vec!();

    for handle in handles {
        let (debug_file_name, symbols) = handle.join().unwrap();

        for symbol in symbols {
            symbolicated_stacks.push(
                format!("{} (in {})", symbol, debug_file_name)
            );
        }
        result.knownModules.push(true);
    }

    // the required result format requires this to be a vec-of-vecs
    result.symbolicatedStacks.push(symbolicated_stacks);

    json::encode(&result).unwrap()
}

/**
  * Returns individual values from the configuration file.
  */
fn get_config(value_name: &str) -> String {
    // TODO move to actual file, static str for the moment
    let toml: &'static str = r#"
        [symbol_urls]
        public = "https://s3-us-west-2.amazonaws.com/org.mozilla.crash-stats.symbols-public/v1"
    "#;
    // TODO support multiple URLs
    let value: toml::Value = toml.parse().unwrap();

    value.lookup(value_name).unwrap().as_str().unwrap().to_string()
}
<|endoftext|>


<commit_before>mod cli;

use cli::Cli;
use petname::Petnames;

use std::collections::HashSet;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use std::process;

use clap::Parser;
use rand::seq::IteratorRandom;

fn main() {
    let cli = Cli::parse();
    match run(cli) {
        Ok(()) | Err(Error::Disconnected) => {
            process::exit(0);
        }
        Err(e) => {
            eprintln!("Error: {}", e);
            process::exit(1);
        }
    }
}

enum Error {
    Io(io::Error),
    FileIo(path::PathBuf, io::Error),
    Cardinality(String),
    Alliteration(String),
    Disconnected,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::Io(ref e) => write!(f, "{}", e),
            Error::FileIo(ref path, ref e) => write!(f, "{}: {}", e, path.display()),
            Error::Cardinality(ref message) => write!(f, "cardinality is zero: {}", message),
            Error::Alliteration(ref message) => write!(f, "cannot alliterate: {}", message),
            Error::Disconnected => write!(f, "caller disconnected / stopped reading"),
        }
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error::Io(error)
    }
}

fn run(cli: Cli) -> Result<(), Error> {
    // Load custom word lists, if specified.
    let words = match cli.directory {
        Some(dirname) => Words::load(dirname)?,
        None => Words::Builtin,
    };

    // Select the appropriate word list.
    let mut petnames = match words {
        Words::Custom(ref adjectives, ref adverbs, ref names) => Petnames::init(adjectives, adverbs, names),
        Words::Builtin => match cli.complexity {
            0 => Petnames::small(),
            1 => Petnames::medium(),
            2 => Petnames::large(),
            _ => Petnames::small(),
        },
    };

    // If requested, limit the number of letters.
    let letters = cli.letters;
    if letters != 0 {
        petnames.retain(|s| s.len() <= letters);
    }

    // Check cardinality.
    if petnames.cardinality(cli.words) == 0 {
        return Err(Error::Cardinality("no petnames to choose from; try relaxing constraints".to_string()));
    }

    // We're going to need a source of randomness.
    let mut rng = rand::thread_rng();

    // Handle alliteration, either by eliminating a specified
    // character, or using a random one.
    let alliterate = cli.alliterate || cli.ubuntu || cli.alliterate_with.is_some();
    if alliterate {
        // We choose the first letter from the intersection of the
        // first letters of each word list in `petnames`.
        let firsts = common_first_letters(&petnames.adjectives, &[&petnames.adverbs, &petnames.names]);
        // if a specific character was requested for alliteration,
        // attempt to use it.
        if let Some(c) = cli.alliterate_with {
            if firsts.contains(&c) {
                petnames.retain(|s| s.starts_with(c));
            } else {
                return Err(Error::Alliteration(
                    "no petnames begin with the choosen alliteration character".to_string(),
                ));
            }
        } else {
            // Otherwise choose the first letter at random; fails if
            // there are no letters.
            match firsts.iter().choose(&mut rng) {
                Some(c) => petnames.retain(|s| s.starts_with(*c)),
                None => {
                    return Err(Error::Alliteration(
                        "word lists have no initial letters in common".to_string(),
                    ))
                }
            };
        }
    }

    // Manage stdout.
    let stdout = io::stdout();
    let mut writer = io::BufWriter::new(stdout.lock());

    // Warn that --count=0 is deprecated.
    if cli.count == 0 {
        eprintln!(concat!(
            "Warning: specifying --count=0 to continuously produce petnames is ",
            "deprecated and its behaviour will change in a future version; ",
            "specify --stream instead.",
        ));
    }

    // Stream if count is 0. TODO: Only stream when --stream is specified.
    let count = if cli.stream || cli.count == 0 { None } else { Some(cli.count) };

    // Get an iterator for the names we want to print out.
    if cli.non_repeating {
        printer(&mut writer, petnames.iter_non_repeating(&mut rng, cli.words, &cli.separator), count)
    } else {
        printer(&mut writer, petnames.iter(&mut rng, cli.words, &cli.separator), count)
    }
}

fn printer<OUT, NAMES>(writer: &mut OUT, names: NAMES, count: Option<usize>) -> Result<(), Error>
where
    OUT: io::Write,
    NAMES: Iterator<Item = String>,
{
    match count {
        None => {
            for name in names {
                writeln!(writer, "{}", name).map_err(suppress_disconnect)?;
            }
        }
        Some(n) => {
            for name in names.take(n) {
                writeln!(writer, "{}", name)?;
            }
        }
    }

    Ok(())
}

fn common_first_letters(init: &[&str], more: &[&[&str]]) -> HashSet<char> {
    let mut firsts = first_letters(init);
    let firsts_other: Vec<HashSet<char>> = more.iter().map(|list| first_letters(list)).collect();
    firsts.retain(|c| firsts_other.iter().all(|fs| fs.contains(c)));
    firsts
}

fn first_letters(names: &[&str]) -> HashSet<char> {
    names.iter().filter_map(|s| s.chars().next()).collect()
}

enum Words {
    Custom(String, String, String),
    Builtin,
}

impl Words {
    // Load word lists from the given directory. This function expects to find three
    // files in that directory: `adjectives.txt`, `adverbs.txt`, and `names.txt`.
    // Each should be valid UTF-8, and contain words separated by whitespace.
    fn load<T: AsRef<path::Path>>(dirname: T) -> Result<Self, Error> {
        let dirname = dirname.as_ref();
        Ok(Self::Custom(
            read_file_to_string(dirname.join("adjectives.txt"))?,
            read_file_to_string(dirname.join("adverbs.txt"))?,
            read_file_to_string(dirname.join("names.txt"))?,
        ))
    }
}

fn read_file_to_string<P: AsRef<path::Path>>(path: P) -> Result<String, Error> {
    fs::read_to_string(&path).map_err(|error| Error::FileIo(path.as_ref().to_path_buf(), error))
}

fn suppress_disconnect(err: io::Error) -> Error {
    match err.kind() {
        io::ErrorKind::BrokenPipe => Error::Disconnected,
        _ => err.into(),
    }
}
<commit_msg>Fix typo in error message<commit_after>mod cli;

use cli::Cli;
use petname::Petnames;

use std::collections::HashSet;
use std::fmt;
use std::fs;
use std::io;
use std::path;
use std::process;

use clap::Parser;
use rand::seq::IteratorRandom;

fn main() {
    let cli = Cli::parse();
    match run(cli) {
        Ok(()) | Err(Error::Disconnected) => {
            process::exit(0);
        }
        Err(e) => {
            eprintln!("Error: {}", e);
            process::exit(1);
        }
    }
}

enum Error {
    Io(io::Error),
    FileIo(path::PathBuf, io::Error),
    Cardinality(String),
    Alliteration(String),
    Disconnected,
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::Io(ref e) => write!(f, "{}", e),
            Error::FileIo(ref path, ref e) => write!(f, "{}: {}", e, path.display()),
            Error::Cardinality(ref message) => write!(f, "cardinality is zero: {}", message),
            Error::Alliteration(ref message) => write!(f, "cannot alliterate: {}", message),
            Error::Disconnected => write!(f, "caller disconnected / stopped reading"),
        }
    }
}

impl From<io::Error> for Error {
    fn from(error: io::Error) -> Self {
        Error::Io(error)
    }
}

fn run(cli: Cli) -> Result<(), Error> {
    // Load custom word lists, if specified.
    let words = match cli.directory {
        Some(dirname) => Words::load(dirname)?,
        None => Words::Builtin,
    };

    // Select the appropriate word list.
    let mut petnames = match words {
        Words::Custom(ref adjectives, ref adverbs, ref names) => Petnames::init(adjectives, adverbs, names),
        Words::Builtin => match cli.complexity {
            0 => Petnames::small(),
            1 => Petnames::medium(),
            2 => Petnames::large(),
            _ => Petnames::small(),
        },
    };

    // If requested, limit the number of letters.
    let letters = cli.letters;
    if letters != 0 {
        petnames.retain(|s| s.len() <= letters);
    }

    // Check cardinality.
    if petnames.cardinality(cli.words) == 0 {
        return Err(Error::Cardinality("no petnames to choose from; try relaxing constraints".to_string()));
    }

    // We're going to need a source of randomness.
    let mut rng = rand::thread_rng();

    // Handle alliteration, either by eliminating a specified
    // character, or using a random one.
    let alliterate = cli.alliterate || cli.ubuntu || cli.alliterate_with.is_some();
    if alliterate {
        // We choose the first letter from the intersection of the
        // first letters of each word list in `petnames`.
        let firsts = common_first_letters(&petnames.adjectives, &[&petnames.adverbs, &petnames.names]);
        // if a specific character was requested for alliteration,
        // attempt to use it.
        if let Some(c) = cli.alliterate_with {
            if firsts.contains(&c) {
                petnames.retain(|s| s.starts_with(c));
            } else {
                return Err(Error::Alliteration(
                    "no petnames begin with the chosen alliteration character".to_string(),
                ));
            }
        } else {
            // Otherwise choose the first letter at random; fails if
            // there are no letters.
            match firsts.iter().choose(&mut rng) {
                Some(c) => petnames.retain(|s| s.starts_with(*c)),
                None => {
                    return Err(Error::Alliteration(
                        "word lists have no initial letters in common".to_string(),
                    ))
                }
            };
        }
    }

    // Manage stdout.
    let stdout = io::stdout();
    let mut writer = io::BufWriter::new(stdout.lock());

    // Warn that --count=0 is deprecated.
    if cli.count == 0 {
        eprintln!(concat!(
            "Warning: specifying --count=0 to continuously produce petnames is ",
            "deprecated and its behaviour will change in a future version; ",
            "specify --stream instead.",
        ));
    }

    // Stream if count is 0. TODO: Only stream when --stream is specified.
    let count = if cli.stream || cli.count == 0 { None } else { Some(cli.count) };

    // Get an iterator for the names we want to print out.
    if cli.non_repeating {
        printer(&mut writer, petnames.iter_non_repeating(&mut rng, cli.words, &cli.separator), count)
    } else {
        printer(&mut writer, petnames.iter(&mut rng, cli.words, &cli.separator), count)
    }
}

fn printer<OUT, NAMES>(writer: &mut OUT, names: NAMES, count: Option<usize>) -> Result<(), Error>
where
    OUT: io::Write,
    NAMES: Iterator<Item = String>,
{
    match count {
        None => {
            for name in names {
                writeln!(writer, "{}", name).map_err(suppress_disconnect)?;
            }
        }
        Some(n) => {
            for name in names.take(n) {
                writeln!(writer, "{}", name)?;
            }
        }
    }

    Ok(())
}

fn common_first_letters(init: &[&str], more: &[&[&str]]) -> HashSet<char> {
    let mut firsts = first_letters(init);
    let firsts_other: Vec<HashSet<char>> = more.iter().map(|list| first_letters(list)).collect();
    firsts.retain(|c| firsts_other.iter().all(|fs| fs.contains(c)));
    firsts
}

fn first_letters(names: &[&str]) -> HashSet<char> {
    names.iter().filter_map(|s| s.chars().next()).collect()
}

enum Words {
    Custom(String, String, String),
    Builtin,
}

impl Words {
    // Load word lists from the given directory. This function expects to find three
    // files in that directory: `adjectives.txt`, `adverbs.txt`, and `names.txt`.
    // Each should be valid UTF-8, and contain words separated by whitespace.
    fn load<T: AsRef<path::Path>>(dirname: T) -> Result<Self, Error> {
        let dirname = dirname.as_ref();
        Ok(Self::Custom(
            read_file_to_string(dirname.join("adjectives.txt"))?,
            read_file_to_string(dirname.join("adverbs.txt"))?,
            read_file_to_string(dirname.join("names.txt"))?,
        ))
    }
}

fn read_file_to_string<P: AsRef<path::Path>>(path: P) -> Result<String, Error> {
    fs::read_to_string(&path).map_err(|error| Error::FileIo(path.as_ref().to_path_buf(), error))
}

fn suppress_disconnect(err: io::Error) -> Error {
    match err.kind() {
        io::ErrorKind::BrokenPipe => Error::Disconnected,
        _ => err.into(),
    }
}
<|endoftext|>


<commit_before>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015-2016 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! # SOS kernel
//! This crate contains the kernel for SOS, the Stupid Operating System.
//!
//! # SOS: the Stupid Operating System
//! SOS is a simple, tiny toy OS implemented in Rust. It targets the x86,
//! x86_64, and ARM v7 CPU architectures.
//!
//! I'm writing this mostly for fun, to learn more about OS design and kernel
//! hacking, so don't expect anything new or exciting out of this project.
//!
//! SOS is copyright 2015-2016 Eliza Weisman, and is released under the terms
//! of the MIT license.

#![crate_name = "sos_kernel"]

#![doc(html_root_url = "https://hawkw.github.io/sos-kernel/")]

#![feature( lang_items, asm, naked_functions )]
#![feature( linkage )]
#![feature( const_fn
          , slice_patterns
          , associated_consts
          , type_ascription
          , custom_derive )]
#![feature(collections)]

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#![no_std]
#![cfg_attr(not(test), no_main)]

// -- non-SOS dependencies --------------------------------------------------
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate bitflags;
#[macro_use] extern crate log;

extern crate collections;
extern crate rlibc;
extern crate spin;

// -- SOS dependencies ------------------------------------------------------
#[macro_use] extern crate vga;
#[macro_use] extern crate cpu;

extern crate util;
extern crate alloc;
extern crate memory;
extern crate elf;
extern crate paging;
extern crate params;

#[macro_use] pub mod io;

pub mod heap;
pub mod arch;
pub mod logger;

use params::InitParams;

#[macro_use]
macro_rules! init_log {
    (f$dots:expr, $task:expr, $msg:expr) => (
        println!( "{task:<40}{res:>38}\n{msg:>.width$}"
                , task = format!("{:>.width$}", $task, width = $dots)
                , res = "[ FAIL ]"
                , msg = $msg
                , width = $dots + 1
                )
    );
    (fail: $dots:expr, $task:expr) => (
            println!( "{task:<40}{res:>38}"
                    , task = format!("{:>.width$}", $task, width = $dots)
                    , res = "[ FAIL ]"
                    )
    );
    (okay: $dots:expr, $task:expr, $msg:expr) => (
        println!( "{task:<40}{res:>38}\n{msg:>.width$}"
                , task = format!("{:>.width$}", $task, width = $dots)
                , res = "[ OKAY ]"
                , msg = $msg
                , width = $dots + 1
                )
    );
    (okay: $dots:expr, $task:expr) => (
            println!( "{task:<40}{res:>38}"
                    , task = format!("{:>.width$}", $task, width = $dots)
                    , res = "[ OKAY ]"
                    )
    );
}

/// SOS version number
pub const VERSION_STRING: &'static str
    = concat!("Stupid Operating System v", env!("CARGO_PKG_VERSION"));

/// Kernel main loop
pub fn kernel_main() -> ! {
    let mut a_vec = collections::vec::Vec::<usize>::new();
    info!(target: "test", "Created a vector in kernel space! {:?}", a_vec);
    a_vec.push(1);
    info!(target: "test", "pushed to vec: {:?}", a_vec);
    a_vec.push(2);
    info!(target: "test", "pushed to vec: {:?}", a_vec);

    // let mut frame_allocator = frame_alloc::BuddyFrameAllocator::new();
    // paging::test_paging(&mut frame_allocator);

    loop { }
}

/// Kernel initialization function called into by architecture-specific init
///
/// Our initialization process essentially looks like this:
///
/// ```text
/// +------------+ +----------+ +----------------------------------------------+
/// | bootloader | | assembly | | rust                                         |
/// | (multiboot) -> start.asm -> arch_init() -> kernel_init() -> kernel_main()|
/// +------------+ +----------+ +----------------------------------------------+
/// ```
pub fn kernel_init(params: InitParams) {
    kinfoln!("Hello from the kernel!");
    // -- initialize interrupts ----------------------------------------------
    kinfoln!(dots: " . ", "Initializing interrupts:");
    unsafe {
        arch::interrupts::initialize();
    };
    kinfoln!(dots: " . ", target: "Enabling interrupts", "[ OKAY ]");

    // -- initialize the heap ------------------------------------------------
    if let Ok(_) =  unsafe { heap::initialize(&params) } {
        kinfoln!( dots: " . ", target: "Intializing heap"
                , "[ OKAY ]"
                );
        kinfoln!( dots: " . . "
                , "Heap begins at {:#x} and ends at {:#x}"
                , params.heap_base, params.heap_top);
    } else {
        kinfoln!( dots: " . ", target: "Intializing heap"
                , "[ FAIL ]"
                );
    }

    // -- remap the kernel ----------------------------------------------------
    kinfoln!(dots: " . ", "Remapping the kernel:");

    let frame_allocator = alloc::buddy::BuddyFrameAllocator::new();
    ::paging::kernel_remap(&params, &frame_allocator);

    kinfoln!( dots: " . ", target: "Remapping the kernel", "[ OKAY ]");

    println!("\n{} {}-bit\n", VERSION_STRING, arch::ARCH_BITS);

    // -- call into kernel main loop ------------------------------------------
    // (currently, this does nothing)
    kernel_main()
}

/// This fake `main` function exists only to placate `cargo test`.
#[cfg(test)]
fn main() {

}
<commit_msg>[kernel] add more information to bad ASCII graphic<commit_after>//
//  SOS: the Stupid Operating System
//  by Eliza Weisman (hi@hawkweisman.me)
//
//  Copyright (c) 2015-2016 Eliza Weisman
//  Released under the terms of the MIT license. See `LICENSE` in the root
//  directory of this repository for more information.
//
//! # SOS kernel
//! This crate contains the kernel for SOS, the Stupid Operating System.
//!
//! # SOS: the Stupid Operating System
//! SOS is a simple, tiny toy OS implemented in Rust. It targets the x86,
//! x86_64, and ARM v7 CPU architectures.
//!
//! I'm writing this mostly for fun, to learn more about OS design and kernel
//! hacking, so don't expect anything new or exciting out of this project.
//!
//! SOS is copyright 2015-2016 Eliza Weisman, and is released under the terms
//! of the MIT license.

#![crate_name = "sos_kernel"]

#![doc(html_root_url = "https://hawkw.github.io/sos-kernel/")]

#![feature( lang_items, asm, naked_functions )]
#![feature( linkage )]
#![feature( const_fn
          , slice_patterns
          , associated_consts
          , type_ascription
          , custom_derive )]
#![feature(collections)]

#![cfg_attr(feature="clippy", feature(plugin))]
#![cfg_attr(feature="clippy", plugin(clippy))]

#![no_std]
#![cfg_attr(not(test), no_main)]

// -- non-SOS dependencies --------------------------------------------------
#[macro_use] extern crate lazy_static;
#[macro_use] extern crate bitflags;
#[macro_use] extern crate log;

extern crate collections;
extern crate rlibc;
extern crate spin;

// -- SOS dependencies ------------------------------------------------------
#[macro_use] extern crate vga;
#[macro_use] extern crate cpu;

extern crate util;
extern crate alloc;
extern crate memory;
extern crate elf;
extern crate paging;
extern crate params;

#[macro_use] pub mod io;

pub mod heap;
pub mod arch;
pub mod logger;

use params::InitParams;

#[macro_use]
macro_rules! init_log {
    (f$dots:expr, $task:expr, $msg:expr) => (
        println!( "{task:<40}{res:>38}\n{msg:>.width$}"
                , task = format!("{:>.width$}", $task, width = $dots)
                , res = "[ FAIL ]"
                , msg = $msg
                , width = $dots + 1
                )
    );
    (fail: $dots:expr, $task:expr) => (
            println!( "{task:<40}{res:>38}"
                    , task = format!("{:>.width$}", $task, width = $dots)
                    , res = "[ FAIL ]"
                    )
    );
    (okay: $dots:expr, $task:expr, $msg:expr) => (
        println!( "{task:<40}{res:>38}\n{msg:>.width$}"
                , task = format!("{:>.width$}", $task, width = $dots)
                , res = "[ OKAY ]"
                , msg = $msg
                , width = $dots + 1
                )
    );
    (okay: $dots:expr, $task:expr) => (
            println!( "{task:<40}{res:>38}"
                    , task = format!("{:>.width$}", $task, width = $dots)
                    , res = "[ OKAY ]"
                    )
    );
}

/// SOS version number
pub const VERSION_STRING: &'static str
    = concat!("Stupid Operating System v", env!("CARGO_PKG_VERSION"));

/// Kernel main loop
pub fn kernel_main() -> ! {
    let mut a_vec = collections::vec::Vec::<usize>::new();
    info!(target: "test", "Created a vector in kernel space! {:?}", a_vec);
    a_vec.push(1);
    info!(target: "test", "pushed to vec: {:?}", a_vec);
    a_vec.push(2);
    info!(target: "test", "pushed to vec: {:?}", a_vec);

    // let mut frame_allocator = frame_alloc::BuddyFrameAllocator::new();
    // paging::test_paging(&mut frame_allocator);

    loop { }
}

/// Kernel initialization function called into by architecture-specific init
///
/// Our initialization process essentially looks like this:
///
/// ```text
/// +-------------+
/// | bootloader  |
/// | (multiboot) |
/// +------|------+
/// +------V------+
/// | start.asm   |
/// +------|------+
/// +------|--------------------------------------------------------+
/// |      |           RUST-LAND KERNEL FUNCTIONS                   |
/// |      V                                                        |
/// | arch_init() ----------> kernel_init() --------> kernel_main() |
/// | + collects boot info   + initializes interrupts               |
/// |   from arch-specific   + initializes the heap                 |
/// |   sources              + remaps the kernel into the higher    |
/// | + some CPU-specific      half of the address space            |
/// |   configuration                                               |
/// +---------------------------------------------------------------+
/// ```
pub fn kernel_init(params: InitParams) {
    kinfoln!("Hello from the kernel!");
    // -- initialize interrupts ----------------------------------------------
    kinfoln!(dots: " . ", "Initializing interrupts:");
    unsafe {
        arch::interrupts::initialize();
    };
    kinfoln!(dots: " . ", target: "Enabling interrupts", "[ OKAY ]");

    // -- initialize the heap ------------------------------------------------
    if let Ok(_) =  unsafe { heap::initialize(&params) } {
        kinfoln!( dots: " . ", target: "Intializing heap"
                , "[ OKAY ]"
                );
        kinfoln!( dots: " . . "
                , "Heap begins at {:#x} and ends at {:#x}"
                , params.heap_base, params.heap_top);
    } else {
        kinfoln!( dots: " . ", target: "Intializing heap"
                , "[ FAIL ]"
                );
    }

    // -- remap the kernel ----------------------------------------------------
    kinfoln!(dots: " . ", "Remapping the kernel:");

    let frame_allocator = alloc::buddy::BuddyFrameAllocator::new();
    ::paging::kernel_remap(&params, &frame_allocator);

    kinfoln!( dots: " . ", target: "Remapping the kernel", "[ OKAY ]");

    println!("\n{} {}-bit\n", VERSION_STRING, arch::ARCH_BITS);

    // -- call into kernel main loop ------------------------------------------
    // (currently, this does nothing)
    kernel_main()
}

/// This fake `main` function exists only to placate `cargo test`.
#[cfg(test)]
fn main() {

}
<|endoftext|>