<commit_before>#[macro_use]
extern crate error_chain;

use game::{self, Game, GameConfig};
use log::info;
use std::path::PathBuf;
use structopt::StructOpt;
use wad::Archive;

use self::errors::Result;

#[derive(StructOpt)]
#[structopt(
    name = "Rusty Doom",
    raw(setting = "structopt::clap::AppSettings::ColoredHelp")
)]
struct App {
    #[structopt(
        short = "i",
        long = "iwad",
        default_value = "doom1.wad",
        value_name = "FILE",
        parse(from_os_str)
    )]
    /// Initial WAD file to use.
    iwad: PathBuf,

    #[structopt(
        short = "m",
        long = "metadata",
        default_value = "assets/meta/doom.toml",
        value_name = "FILE",
        parse(from_os_str)
    )]
    /// Path to TOML metadata file.
    metadata: PathBuf,

    #[structopt(
        short = "r",
        long = "resolution",
        default_value = "1280x720",
        value_name = "WIDTHxHEIGHT",
        parse(try_from_str = "parse_resolution")
    )]
    /// Size of the game window.
    resolution: (u32, u32),

    #[structopt(
        short = "l",
        long = "level",
        default_value = "0",
        help = "the index of the level to render",
        value_name = "N"
    )]
    /// The index of the level to render (0-based).
    level_index: usize,

    #[structopt(
        short = "f",
        long = "fov",
        default_value = "65",
        value_name = "DEGREES"
    )]
    /// Horizontal field of view.
    fov: f32,

    #[structopt(subcommand)]
    command: Option<Command>,
}

#[derive(StructOpt, Copy, Clone)]
enum Command {
    /// Load metadata and all levels in WAD, then exit.
    #[structopt(name = "check")]
    Check,

    /// List the names and indices of all the leves in the WAD, then exit.
    #[structopt(name = "list-levels")]
    ListLevelNames,
}

impl App {
    pub fn run_from_args() -> Result<()> {
        Self::from_args().run()
    }

    pub fn run(self) -> Result<()> {
        env_logger::Builder::from_env(
            env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
        )
        .default_format_timestamp(false)
        .init();

        match self.command {
            None => {
                let mut game = game::create(&self.into_config())?;
                game.run()?;
                info!("Game main loop ended, shutting down...");
            }
            Some(Command::Check) => {
                let mut game = game::create(&GameConfig {
                    initial_level_index: 0,
                    ..self.into_config()
                })?;
                info!("Loading all levels...");
                let t0 = time::precise_time_s();
                for level_index in 1..game.num_levels() {
                    game.load_level(level_index)?;
                }
                info!(
                    "Done loading all levels in {:.4}s. Shutting down...",
                    time::precise_time_s() - t0
                );
            }
            Some(Command::ListLevelNames) => {
                let wad = Archive::open(&self.iwad, &self.metadata)?;
                for i_level in 0..wad.num_levels() {
                    println!("{:3} {:8}", i_level, wad.level_lump(i_level)?.name());
                }
            }
        }
        info!("Clean shutdown.");
        Ok(())
    }

    fn into_config(self) -> GameConfig {
        GameConfig {
            wad_file: self.iwad,
            metadata_file: self.metadata,
            fov: self.fov,
            width: self.resolution.0,
            height: self.resolution.1,
            version: env!("CARGO_PKG_VERSION"),
            initial_level_index: self.level_index,
        }
    }
}

/// Parse a resolution string like `WIDTHxHEIGHT` into `(width, height)`.
fn parse_resolution(size_str: &str) -> Result<(u32, u32)> {
    let size_if_ok = size_str
        .find('x')
        .and_then(|x_index| {
            if x_index == 0 || x_index + 1 == size_str.len() {
                None
            } else {
                Some((&size_str[..x_index], &size_str[x_index + 1..]))
            }
        })
        .map(|(width, height)| (width.parse::<u32>(), height.parse::<u32>()))
        .and_then(|size| match size {
            (Ok(width), Ok(height)) => Some((width, height)),
            _ => None,
        });

    if let Some(size) = size_if_ok {
        Ok(size)
    } else {
        bail!("resolution format must be WIDTHxHEIGHT");
    }
}

mod errors {
    error_chain! {
        errors {}
        links {
            Engine(::engine::Error, ::engine::ErrorKind);
            Game(::game::Error, ::game::ErrorKind);
            Wad(::wad::Error, ::wad::ErrorKind);
        }
    }
}

quick_main!(App::run_from_args);
<commit_msg>main: add a few more docs<commit_after>#[macro_use]
extern crate error_chain;

use game::{self, Game, GameConfig};
use log::info;
use std::path::PathBuf;
use structopt::StructOpt;
use wad::Archive;

use self::errors::Result;

#[derive(StructOpt)]
#[structopt(
    name = "Rusty Doom",
    raw(setting = "structopt::clap::AppSettings::ColoredHelp")
)]
struct App {
    #[structopt(
        short = "i",
        long = "iwad",
        default_value = "doom1.wad",
        value_name = "FILE",
        parse(from_os_str)
    )]
    /// Initial WAD file to use.
    iwad: PathBuf,

    #[structopt(
        short = "m",
        long = "metadata",
        default_value = "assets/meta/doom.toml",
        value_name = "FILE",
        parse(from_os_str)
    )]
    /// Path to TOML metadata file.
    metadata: PathBuf,

    #[structopt(
        short = "r",
        long = "resolution",
        default_value = "1280x720",
        value_name = "WIDTHxHEIGHT",
        parse(try_from_str = "parse_resolution")
    )]
    /// Size of the game window.
    resolution: (u32, u32),

    #[structopt(
        short = "l",
        long = "level",
        default_value = "0",
        help = "the index of the level to render",
        value_name = "N"
    )]
    /// The index of the level to render (0-based).
    level_index: usize,

    #[structopt(
        short = "f",
        long = "fov",
        default_value = "65",
        value_name = "DEGREES"
    )]
    /// Horizontal field of view.
    fov: f32,

    #[structopt(subcommand)]
    command: Option<Command>,
}

#[derive(StructOpt, Copy, Clone)]
enum Command {
    /// Load metadata and all levels in WAD, then exit.
    #[structopt(name = "check")]
    Check,

    /// List the names and indices of all the leves in the WAD, then exit.
    #[structopt(name = "list-levels")]
    ListLevelNames,
}

impl App {
    /// Parse options from command-line arguments and run.
    pub fn run_from_args() -> Result<()> {
        Self::from_args().run()
    }

    /// Either play the game (if no `Command` was passed), or perform the command.
    pub fn run(self) -> Result<()> {
        // Init logging, with default `info` level.
        env_logger::Builder::from_env(
            env_logger::Env::default().filter_or(env_logger::DEFAULT_FILTER_ENV, "info"),
        )
        .default_format_timestamp(false)
        .init();

        match self.command {
            None => {
                let mut game = game::create(&self.into_config())?;
                game.run()?;
                info!("Game main loop ended, shutting down...");
            }
            Some(Command::Check) => {
                let mut game = game::create(&GameConfig {
                    initial_level_index: 0,
                    ..self.into_config()
                })?;
                info!("Loading all levels...");
                let t0 = time::precise_time_s();
                for level_index in 1..game.num_levels() {
                    game.load_level(level_index)?;
                }
                info!(
                    "Done loading all levels in {:.4}s. Shutting down...",
                    time::precise_time_s() - t0
                );
            }
            Some(Command::ListLevelNames) => {
                let wad = Archive::open(&self.iwad, &self.metadata)?;
                for i_level in 0..wad.num_levels() {
                    println!("{:3} {:8}", i_level, wad.level_lump(i_level)?.name());
                }
            }
        }
        info!("Clean shutdown.");
        Ok(())
    }

    /// Populate `GameConfig` fields from the parsed command-line arguments.
    fn into_config(self) -> GameConfig {
        GameConfig {
            wad_file: self.iwad,
            metadata_file: self.metadata,
            fov: self.fov,
            width: self.resolution.0,
            height: self.resolution.1,
            version: env!("CARGO_PKG_VERSION"),
            initial_level_index: self.level_index,
        }
    }
}

/// Parse a resolution string like `WIDTHxHEIGHT` into `(width, height)`.
fn parse_resolution(size_str: &str) -> Result<(u32, u32)> {
    let size_if_ok = size_str
        .find('x')
        .and_then(|x_index| {
            if x_index == 0 || x_index + 1 == size_str.len() {
                None
            } else {
                Some((&size_str[..x_index], &size_str[x_index + 1..]))
            }
        })
        .map(|(width, height)| (width.parse::<u32>(), height.parse::<u32>()))
        .and_then(|size| match size {
            (Ok(width), Ok(height)) => Some((width, height)),
            _ => None,
        });

    if let Some(size) = size_if_ok {
        Ok(size)
    } else {
        bail!("Resolution format must be WIDTHxHEIGHT");
    }
}

mod errors {
    error_chain! {
        errors {}
        links {
            Engine(::engine::Error, ::engine::ErrorKind);
            Game(::game::Error, ::game::ErrorKind);
            Wad(::wad::Error, ::wad::ErrorKind);
        }
    }
}

quick_main!(App::run_from_args);
<|endoftext|>


<commit_before>//! # Telegram Groups Bot
//!
//! This bot can be used to manage multiple Telegram topic group chats.
//! Admins can register topic channels, which the users can then list.
//! The users can also request the invite link for one of the topic channels.
//!
//! ## Command API
//!
//! - `/help` Show help
//! - `/groups` Show list of available topic groups
//! - `/join <topic>` Show the invite link for that group
//!
//! ## Implementation Details
//!
//! This bot builds on top of the [telegram-bot][0] crate. It listens to the Telegram API through
//! LongPoll.
//!
//! When a message comes in, it is first parsed into a `Command`. If that worked out, the command
//! is dispatched to a `CommandHandler`.
//!
//! All `CommandHandler`s run in a thread pool, so that they don't block the entire bot.
//!
//! [0]: https://crates.io/crates/telegram-bot

extern crate telegram_bot;
extern crate threadpool;
extern crate conv;
#[macro_use] extern crate log;
extern crate env_logger;

pub mod types;
pub mod errors;
pub mod commands;

use std::process::exit;

use telegram_bot::{Api, Listener, ListeningMethod, MessageType, ListeningAction};
use threadpool::ThreadPool;
use conv::TryFrom;

use types::Command;
use commands::CommandHandler;

/// Initialize and return a `telegram_bot::Listener` instance.
fn get_listener() -> Listener {

    let api = Api::from_env("TELEGRAM_BOT_TOKEN").unwrap_or_else(|_| {
        println!("Error: TELEGRAM_BOT_TOKEN env var missing");
        exit(1);
    });

    match api.get_me() {
        Ok(user) => println!("Starting {}...", user.first_name),
        Err(e) => {
            println!("Error: Could not fetch information about Telegram bot.");
            println!("  Error Details: {:?}", e);
            println!("  Maybe check your TELEGRAM_BOT_TOKEN env var?");
            exit(1);
        },
    }

    api.listener(ListeningMethod::LongPoll(None))

}

fn main() {

    // Initialize env logger
    env_logger::init().unwrap();

    // Get Telegram Api listener
    let mut listener = get_listener();

    // Create thread pool for command handlers
    let pool = ThreadPool::new(12);

    // Fetch new updates via long poll method
    listener.listen(|u| {

        // Dispatch messages
        if let Some(m) = u.message {

            // Process text messages
            if let MessageType::Text(text) = m.msg {

                // Dispatch command handlers
                let command = Command::try_from(&*text);
                match command {
                    Ok(cmd) => {
                        debug!("Command: {:?}", cmd);
                        let handler = commands::LogHandler { command: cmd.clone() };
                        pool.execute(move || {
                            handler.handle();
                        });
                    }
                    Err(_) => debug!("No command."),
                }

            } else {
                println!("Other msg was {:?}", m.msg);
            }
        }

        // If none of the "try!" statements returned an error: It's Ok!
        Ok(ListeningAction::Continue)
    });

}
<commit_msg>Move API outside of get_listener<commit_after>//! # Telegram Groups Bot
//!
//! This bot can be used to manage multiple Telegram topic group chats.
//! Admins can register topic channels, which the users can then list.
//! The users can also request the invite link for one of the topic channels.
//!
//! ## Command API
//!
//! - `/help` Show help
//! - `/groups` Show list of available topic groups
//! - `/join <topic>` Show the invite link for that group
//!
//! ## Implementation Details
//!
//! This bot builds on top of the [telegram-bot][0] crate. It listens to the Telegram API through
//! LongPoll.
//!
//! When a message comes in, it is first parsed into a `Command`. If that worked out, the command
//! is dispatched to a `CommandHandler`.
//!
//! All `CommandHandler`s run in a thread pool, so that they don't block the entire bot.
//!
//! [0]: https://crates.io/crates/telegram-bot

extern crate telegram_bot;
extern crate threadpool;
extern crate conv;
#[macro_use] extern crate log;
extern crate env_logger;

pub mod types;
pub mod errors;
pub mod commands;

use std::process::exit;

use telegram_bot::{Api, Listener, ListeningMethod, MessageType, ListeningAction};
use threadpool::ThreadPool;
use conv::TryFrom;

use types::Command;
use commands::CommandHandler;

/// Initialize and return a `telegram_bot::Listener` instance.
fn get_listener(api: &Api) -> Listener {
    match api.get_me() {
        Ok(user) => println!("Starting {}...", user.first_name),
        Err(e) => {
            println!("Error: Could not fetch information about Telegram bot.");
            println!("  Error Details: {:?}", e);
            println!("  Maybe check your TELEGRAM_BOT_TOKEN env var?");
            exit(1);
        },
    }

    api.listener(ListeningMethod::LongPoll(None))

}

fn main() {

    // Initialize env logger
    env_logger::init().unwrap();

    // Get Telegram Api listener
    let api = Api::from_env("TELEGRAM_BOT_TOKEN").unwrap_or_else(|_| {
        println!("Error: TELEGRAM_BOT_TOKEN env var missing");
        exit(1);
    });
    let mut listener = get_listener(&api);

    // Create thread pool for command handlers
    let pool = ThreadPool::new(12);

    // Fetch new updates via long poll method
    listener.listen(|u| {

        // Dispatch messages
        if let Some(m) = u.message {

            // Process text messages
            if let MessageType::Text(text) = m.msg {

                // Dispatch command handlers
                let command = Command::try_from(&*text);
                match command {
                    Ok(cmd) => {
                        debug!("Command: {:?}", cmd);
                        let handler = commands::LogHandler { command: cmd.clone() };
                        pool.execute(move || {
                            handler.handle();
                        });
                    }
                    Err(_) => debug!("No command."),
                }

            } else {
                println!("Other msg was {:?}", m.msg);
            }
        }

        // If none of the "try!" statements returned an error: It's Ok!
        Ok(ListeningAction::Continue)
    });

}
<|endoftext|>


<commit_before>#![feature(plugin)]
#![plugin(regex_macros)]

extern crate argparse;
extern crate html5ever;
extern crate html5ever_dom_sink;
extern crate hyper;
extern crate iron;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate regex;
extern crate rustc_serialize;
extern crate tempfile;
extern crate tendril;
extern crate url;

/// Helper macro for unwrapping Result values whose E types implement std::fmt::Display. For Ok(),
/// evaluates to the contained value. For Err(), returns early with an Err containing the formatted
/// error.
macro_rules! try_display {
    ($expr:expr, $($format_arg:expr),* ) => (match $expr {
        Ok(val) => val,
        Err(err) => return Err(format!("{}: {}", format!($($format_arg),*), err)),
    })
}

mod json;
mod wiki;

use argparse::ArgumentParser;
use argparse::Store;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;
use std::str::FromStr;

use html5ever::Attribute;
use html5ever::tree_builder::interface::TreeSink;
use html5ever_dom_sink::common::NodeEnum;
use html5ever_dom_sink::rcdom::Handle;
use html5ever_dom_sink::rcdom::RcDom;
use hyper::Client;
use hyper::header::Connection;
use iron::Iron;
use iron::IronResult;
use iron::Request;
use iron::Response;
use iron::headers::ContentType;
use iron::middleware::Handler;
use iron::mime::Mime;
use iron::mime::SubLevel;
use iron::mime::TopLevel;
use tempfile::NamedTempFile;

use wiki::Revision;
use wiki::Wiki;

// TODO: consider doing s/en.wikipedia.org/this app's url/ on the HTML before serving it. This
// currently works fine, but might not over HTTPS.

/// Does a 3-way merge, merging `new1` and `new2` under the assumption that both diverged from
/// `old`. Returns None if the strings do not merge together cleanly.
fn merge(old: &str, new1: &str, new2: &str) -> Result<Option<String>, String> {
    fn write_to_temp_file(contents: &str) -> Result<NamedTempFile, String> {
        let tempfile = try_display!(NamedTempFile::new(), "Error creating temp file");
        let mut file = try_display!(OpenOptions::new().write(true).open(tempfile.path()),
                                    "Error opening temp file for writing");
        try_display!(file.write_all(contents.as_bytes()), "Error writing to temp file");
        try_display!(file.flush(), "Error flushing temp file");
        Ok(tempfile)
    }

    let old_tempfile = try!(write_to_temp_file(old));
    let new1_tempfile = try!(write_to_temp_file(new1));
    let new2_tempfile = try!(write_to_temp_file(new2));

    let mut process = Command::new("diff3");
    process.arg("-m").args(&[new1_tempfile.path(), old_tempfile.path(), new2_tempfile.path()])
        .stdout(Stdio::piped()).stderr(Stdio::null());
    let output = try_display!(process.output(), "Error getting output from diff3 subprocess");
    if output.status.success() {
        Ok(Some(try_display!(String::from_utf8(output.stdout),
                             "Error converting diff3 output to UTF-8")))
    } else {
        Ok(None)
    }
}

fn replace_node_with_placeholder(original_html: &str, div_id: &str, placeholder: &str)
    -> Result<String, String> {
    let html = tendril::StrTendril::from_str(original_html).unwrap();
    let mut dom: RcDom = html5ever::parse(html5ever::one_input(html), Default::default());

    let handle = try!(find_node_by_id(&dom.get_document(), div_id));
    let child_handles =
        (&handle.borrow().children).into_iter().map(|child| child.clone()).collect::<Vec<_>>();
    for child_handle in child_handles {
        dom.remove_from_parent(child_handle);
    }
    dom.append(handle,
               html5ever::tree_builder::interface::NodeOrText::AppendText(
                   tendril::StrTendril::from_str(placeholder).unwrap()));
    let mut serialized: Vec<u8> = vec![];
    html5ever::serialize::serialize(&mut serialized, &dom.document, Default::default());
    Ok(try_display!(String::from_utf8(serialized),
                    "Error converting serialized HTML to UTF-8 string"))
}

fn find_node_by_id(handle: &Handle, id: &str) -> Result<Handle, String> {
    fn has_matching_id(attributes: &Vec<Attribute>, id: &str) -> bool {
        return attributes.into_iter().any(
            // TODO: do I seriously have to construct a StrTendril here?
            // There has to be a better way.
            |attribute| attribute.name.local.as_slice() == "id" &&
                attribute.value == tendril::StrTendril::from_str(id).unwrap());
    }

    let node = handle.borrow();
    match node.node {
        NodeEnum::Element(_, ref attributes) if has_matching_id(attributes, id) => Ok(handle.clone()),
        _ => {
            for child in &node.children {
                match find_node_by_id(child, id) {
                    // TODO: this looks weird. Is this an abnormal way to do things?
                    Ok(node) => return Ok(node),
                    _ => continue,
                }
            }
            Err(format!("No node with ID {} found", id))
        },
    }
}

// TODO: rename?
struct WikipediaWithoutWikipedians {
    wiki: Wiki,
    client: Client
}

impl WikipediaWithoutWikipedians {
    fn new(wiki: Wiki, client: Client) -> WikipediaWithoutWikipedians {
        WikipediaWithoutWikipedians { wiki: wiki, client: client }
    }

    /// Returns a vector of Revisions representing all reversions of vandalism for the page `title`.
    fn get_vandalism_reversions(&self, title: &str) -> Result<Vec<Revision>, String> {
        // TODO: Add a flag to control number of revisions, and experiment with it.
        let revisions = try!(self.wiki.get_revisions(title, 60));
        Ok(revisions.into_iter().filter(|revision| revision.comment.contains("vandal")).collect())
    }

    // TODO: this name is terrible.
    fn get_page_with_vandalism_restored(&self, title: &str) -> Result<String, String> {
        let canonical_title = try!(self.wiki.get_canonical_title(title));
        info!("Canonical page title for \"{}\" is \"{}\"", title, canonical_title);

        let latest_revid = try!(self.wiki.get_latest_revision(&canonical_title)).revid;
        let mut accumulated_contents =
            try!(self.wiki.get_revision_content(&canonical_title, latest_revid));
        let mut revisions = vec![];
        for revision in try!(self.get_vandalism_reversions(&canonical_title)) {
            let reverted_contents =
                try!(self.wiki.get_revision_content(&canonical_title, revision.revid));
            let vandalized_contents =
                try!(self.wiki.get_revision_content(&canonical_title, revision.parentid));
            match merge(&reverted_contents, &vandalized_contents, &accumulated_contents) {
                Ok(Some(merged_contents)) => {
                    info!(concat!("For page \"{}\", restored vandalism ",
                                  "https://{}/w/index.php?title={}&diff=prev&oldid={}"),
                          &title, self.wiki.hostname, &canonical_title, revision.revid);
                    accumulated_contents = merged_contents;
                    revisions.push(revision.revid);
                }
                Ok(None) => (),
                Err(msg) => return Err(format!("Error merging revision {} of \"{}\": {}",
                                               revision.revid, title, msg))
            }
        }

        let body = try!(self.wiki.parse_wikitext(&canonical_title, &accumulated_contents));
        // Note: "title" rather than "canonical_title", so that redirects look right.
        let current_page_contents = try!(self.wiki.get_current_page_content(&title));
        // TODO: randomize the placeholder string per-request
        let page_contents_with_placeholder =
            try!(replace_node_with_placeholder(
                &current_page_contents, "mw-content-text", "WMW_PLACEHOLDER_TEXT"));
        Ok(page_contents_with_placeholder.replace("WMW_PLACEHOLDER_TEXT", &body))
    }
}

impl Handler for WikipediaWithoutWikipedians {
    fn handle(&self, request: &mut Request) -> IronResult<Response> {
        if request.url.path.len() == 2 && request.url.path[0] == "wiki" {
            let mut response =
                match self.get_page_with_vandalism_restored(&request.url.path[1]) {
                    Ok(page_contents) => Response::with((iron::status::Ok, page_contents)),
                    // TODO: create an Error type to pass around, so this can distinguish different
                    // types of error (if that would be helpful).
                    // TODO: create a better error page
                    Err(msg) => Response::with(
                        (iron::status::InternalServerError, "ERROR")),
                };
            response.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
            Ok(response)
        } else {
            // TODO: should I use an HTTP redirect here instead? Would that work? Would it be desirable?
            // TODO: not good enough. Needs to include query string. Maybe should be moved to wiki module.
            match self.client.get(
                &format!("https://{}/{}", self.wiki.hostname, request.url.path.join("/")))
                .header(Connection::close()).send() {
                    Ok(mut wikipedia_response) => {
                        let mut wikipedia_body: Vec<u8> = Vec::new();
                        wikipedia_response.read_to_end(&mut wikipedia_body);

                        let mut response = Response::with(wikipedia_body);
                        response.status = Some(wikipedia_response.status);
                        response.headers = wikipedia_response.headers.clone();
                        info!("Forwarded request for {} to {}", request.url.path.join("/"),
                              self.wiki.hostname);
                        Ok(response)
                    },
                    Err(error) => {
                        let mut response = Response::with(
                            (iron::status::InternalServerError,
                             format!("ERROR: {}", error)));
                        response.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
                        Ok(response)
                    }
                }
        }
    }
}

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

    let mut port = 3000;
    let mut wiki = "en.wikipedia.org".to_string();
    {
        let mut parser = ArgumentParser::new();
        parser.set_description("TODO: Usage description");
        parser.refer(&mut port).add_option(&["-p", "--port"], Store, "The port to serve HTTP on.");
        parser.refer(&mut wiki).add_option(&["--wiki"], Store, "The wiki to mirror.");
        parser.parse_args_or_exit();
    }
    let wikipedia_without_wikipedians =
        WikipediaWithoutWikipedians::new(Wiki::new(wiki.to_string(), Client::new()), Client::new());
    Iron::new(wikipedia_without_wikipedians).http(("localhost", port)).unwrap();
}

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

    #[test]
    fn test_merge_clean() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed.\n";
        let new2 = "First line changed.\n\nSecond line.\n";
        assert_eq!(Some("First line changed.\n\nSecond line changed.\n".to_string()),
                   merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_merge_conflicting() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed one way.\n";
        let new2 = "First line changed.\n\nSecond line changed a different way.\n";
        assert_eq!(None, merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_merge_special_characters() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line 𐅃.\n";
        let new2 = "First line さようなら.\n\nSecond line.\n";
        assert_eq!(Some("First line さようなら.\n\nSecond line 𐅃.\n".to_string()),
                   merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_replace_html_content() {
        let original_html = "

original text

Other text

";
        let expected_html = "

replaced text

Other text

";
        let processed_html = replace_node_with_placeholder(original_html, "mw-content-text", "replaced text").unwrap();
        assert_eq!(expected_html, processed_html);
    }
}
<commit_msg>Make has_matching_id slightly more readable by using format!() instead of StrTendril::from_str().<commit_after>#![feature(plugin)]
#![plugin(regex_macros)]
#![feature(collections)]

extern crate argparse;
extern crate html5ever;
extern crate html5ever_dom_sink;
extern crate hyper;
extern crate iron;
#[macro_use]
extern crate log;
extern crate log4rs;
extern crate regex;
extern crate rustc_serialize;
extern crate tempfile;
extern crate tendril;
extern crate url;

/// Helper macro for unwrapping Result values whose E types implement std::fmt::Display. For Ok(),
/// evaluates to the contained value. For Err(), returns early with an Err containing the formatted
/// error.
macro_rules! try_display {
    ($expr:expr, $($format_arg:expr),* ) => (match $expr {
        Ok(val) => val,
        Err(err) => return Err(format!("{}: {}", format!($($format_arg),*), err)),
    })
}

mod json;
mod wiki;

use argparse::ArgumentParser;
use argparse::Store;
//use collections::borrow::Borrow;
use std::fs::OpenOptions;
use std::io::Read;
use std::io::Write;
use std::process::Command;
use std::process::Stdio;
use std::str::FromStr;

use html5ever::Attribute;
use html5ever::tree_builder::interface::TreeSink;
use html5ever_dom_sink::common::NodeEnum;
use html5ever_dom_sink::rcdom::Handle;
use html5ever_dom_sink::rcdom::RcDom;
use hyper::Client;
use hyper::header::Connection;
use iron::Iron;
use iron::IronResult;
use iron::Request;
use iron::Response;
use iron::headers::ContentType;
use iron::middleware::Handler;
use iron::mime::Mime;
use iron::mime::SubLevel;
use iron::mime::TopLevel;
use tempfile::NamedTempFile;

use wiki::Revision;
use wiki::Wiki;

// TODO: consider doing s/en.wikipedia.org/this app's url/ on the HTML before serving it. This
// currently works fine, but might not over HTTPS.

/// Does a 3-way merge, merging `new1` and `new2` under the assumption that both diverged from
/// `old`. Returns None if the strings do not merge together cleanly.
fn merge(old: &str, new1: &str, new2: &str) -> Result<Option<String>, String> {
    fn write_to_temp_file(contents: &str) -> Result<NamedTempFile, String> {
        let tempfile = try_display!(NamedTempFile::new(), "Error creating temp file");
        let mut file = try_display!(OpenOptions::new().write(true).open(tempfile.path()),
                                    "Error opening temp file for writing");
        try_display!(file.write_all(contents.as_bytes()), "Error writing to temp file");
        try_display!(file.flush(), "Error flushing temp file");
        Ok(tempfile)
    }

    let old_tempfile = try!(write_to_temp_file(old));
    let new1_tempfile = try!(write_to_temp_file(new1));
    let new2_tempfile = try!(write_to_temp_file(new2));

    let mut process = Command::new("diff3");
    process.arg("-m").args(&[new1_tempfile.path(), old_tempfile.path(), new2_tempfile.path()])
        .stdout(Stdio::piped()).stderr(Stdio::null());
    let output = try_display!(process.output(), "Error getting output from diff3 subprocess");
    if output.status.success() {
        Ok(Some(try_display!(String::from_utf8(output.stdout),
                             "Error converting diff3 output to UTF-8")))
    } else {
        Ok(None)
    }
}

fn replace_node_with_placeholder(original_html: &str, div_id: &str, placeholder: &str)
    -> Result<String, String> {
    let html = tendril::StrTendril::from_str(original_html).unwrap();
    let mut dom: RcDom = html5ever::parse(html5ever::one_input(html), Default::default());

    let handle = try!(find_node_by_id(&dom.get_document(), div_id));
    let child_handles =
        (&handle.borrow().children).into_iter().map(|child| child.clone()).collect::<Vec<_>>();
    for child_handle in child_handles {
        dom.remove_from_parent(child_handle);
    }
    dom.append(handle,
               html5ever::tree_builder::interface::NodeOrText::AppendText(
                   tendril::StrTendril::from_str(placeholder).unwrap()));
    let mut serialized: Vec<u8> = vec![];
    html5ever::serialize::serialize(&mut serialized, &dom.document, Default::default());
    Ok(try_display!(String::from_utf8(serialized),
                    "Error converting serialized HTML to UTF-8 string"))
}

fn find_node_by_id(handle: &Handle, id: &str) -> Result<Handle, String> {
    fn has_matching_id(attributes: &Vec<Attribute>, id: &str) -> bool {
        return attributes.into_iter().any(
            |attribute| attribute.name.local.as_slice() == "id" &&
                format!("{}", attribute.value) == id);
    }

    let node = handle.borrow();
    match node.node {
        NodeEnum::Element(_, ref attributes) if has_matching_id(attributes, id) => Ok(handle.clone()),
        _ => {
            for child in &node.children {
                match find_node_by_id(child, id) {
                    // TODO: this looks weird. Is this an abnormal way to do things?
                    Ok(node) => return Ok(node),
                    _ => continue,
                }
            }
            Err(format!("No node with ID {} found", id))
        },
    }
}

// TODO: rename?
struct WikipediaWithoutWikipedians {
    wiki: Wiki,
    client: Client
}

impl WikipediaWithoutWikipedians {
    fn new(wiki: Wiki, client: Client) -> WikipediaWithoutWikipedians {
        WikipediaWithoutWikipedians { wiki: wiki, client: client }
    }

    /// Returns a vector of Revisions representing all reversions of vandalism for the page `title`.
    fn get_vandalism_reversions(&self, title: &str) -> Result<Vec<Revision>, String> {
        // TODO: Add a flag to control number of revisions, and experiment with it.
        let revisions = try!(self.wiki.get_revisions(title, 60));
        Ok(revisions.into_iter().filter(|revision| revision.comment.contains("vandal")).collect())
    }

    // TODO: this name is terrible.
    fn get_page_with_vandalism_restored(&self, title: &str) -> Result<String, String> {
        let canonical_title = try!(self.wiki.get_canonical_title(title));
        info!("Canonical page title for \"{}\" is \"{}\"", title, canonical_title);

        let latest_revid = try!(self.wiki.get_latest_revision(&canonical_title)).revid;
        let mut accumulated_contents =
            try!(self.wiki.get_revision_content(&canonical_title, latest_revid));
        let mut revisions = vec![];
        for revision in try!(self.get_vandalism_reversions(&canonical_title)) {
            let reverted_contents =
                try!(self.wiki.get_revision_content(&canonical_title, revision.revid));
            let vandalized_contents =
                try!(self.wiki.get_revision_content(&canonical_title, revision.parentid));
            match merge(&reverted_contents, &vandalized_contents, &accumulated_contents) {
                Ok(Some(merged_contents)) => {
                    info!(concat!("For page \"{}\", restored vandalism ",
                                  "https://{}/w/index.php?title={}&diff=prev&oldid={}"),
                          &title, self.wiki.hostname, &canonical_title, revision.revid);
                    accumulated_contents = merged_contents;
                    revisions.push(revision.revid);
                }
                Ok(None) => (),
                Err(msg) => return Err(format!("Error merging revision {} of \"{}\": {}",
                                               revision.revid, title, msg))
            }
        }

        let body = try!(self.wiki.parse_wikitext(&canonical_title, &accumulated_contents));
        // Note: "title" rather than "canonical_title", so that redirects look right.
        let current_page_contents = try!(self.wiki.get_current_page_content(&title));
        // TODO: randomize the placeholder string per-request
        let page_contents_with_placeholder =
            try!(replace_node_with_placeholder(
                &current_page_contents, "mw-content-text", "WMW_PLACEHOLDER_TEXT"));
        Ok(page_contents_with_placeholder.replace("WMW_PLACEHOLDER_TEXT", &body))
    }
}

impl Handler for WikipediaWithoutWikipedians {
    fn handle(&self, request: &mut Request) -> IronResult<Response> {
        if request.url.path.len() == 2 && request.url.path[0] == "wiki" {
            let mut response =
                match self.get_page_with_vandalism_restored(&request.url.path[1]) {
                    Ok(page_contents) => Response::with((iron::status::Ok, page_contents)),
                    // TODO: create an Error type to pass around, so this can distinguish different
                    // types of error (if that would be helpful).
                    // TODO: create a better error page
                    Err(msg) => Response::with(
                        (iron::status::InternalServerError, "ERROR")),
                };
            response.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
            Ok(response)
        } else {
            // TODO: should I use an HTTP redirect here instead? Would that work? Would it be desirable?
            // TODO: not good enough. Needs to include query string. Maybe should be moved to wiki module.
            match self.client.get(
                &format!("https://{}/{}", self.wiki.hostname, request.url.path.join("/")))
                .header(Connection::close()).send() {
                    Ok(mut wikipedia_response) => {
                        let mut wikipedia_body: Vec<u8> = Vec::new();
                        wikipedia_response.read_to_end(&mut wikipedia_body);

                        let mut response = Response::with(wikipedia_body);
                        response.status = Some(wikipedia_response.status);
                        response.headers = wikipedia_response.headers.clone();
                        info!("Forwarded request for {} to {}", request.url.path.join("/"),
                              self.wiki.hostname);
                        Ok(response)
                    },
                    Err(error) => {
                        let mut response = Response::with(
                            (iron::status::InternalServerError,
                             format!("ERROR: {}", error)));
                        response.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
                        Ok(response)
                    }
                }
        }
    }
}

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

    let mut port = 3000;
    let mut wiki = "en.wikipedia.org".to_string();
    {
        let mut parser = ArgumentParser::new();
        parser.set_description("TODO: Usage description");
        parser.refer(&mut port).add_option(&["-p", "--port"], Store, "The port to serve HTTP on.");
        parser.refer(&mut wiki).add_option(&["--wiki"], Store, "The wiki to mirror.");
        parser.parse_args_or_exit();
    }
    let wikipedia_without_wikipedians =
        WikipediaWithoutWikipedians::new(Wiki::new(wiki.to_string(), Client::new()), Client::new());
    Iron::new(wikipedia_without_wikipedians).http(("localhost", port)).unwrap();
}

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

    #[test]
    fn test_merge_clean() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed.\n";
        let new2 = "First line changed.\n\nSecond line.\n";
        assert_eq!(Some("First line changed.\n\nSecond line changed.\n".to_string()),
                   merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_merge_conflicting() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line changed one way.\n";
        let new2 = "First line changed.\n\nSecond line changed a different way.\n";
        assert_eq!(None, merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_merge_special_characters() {
        let old = "First line.\n\nSecond line.\n";
        let new1 = "First line.\n\nSecond line 𐅃.\n";
        let new2 = "First line さようなら.\n\nSecond line.\n";
        assert_eq!(Some("First line さようなら.\n\nSecond line 𐅃.\n".to_string()),
                   merge(old, new1, new2).unwrap());
    }

    #[test]
    fn test_replace_html_content() {
        let original_html = "

original text

Other text

";
        let expected_html = "

replaced text

Other text

";
        let processed_html = replace_node_with_placeholder(original_html, "mw-content-text", "replaced text").unwrap();
        assert_eq!(expected_html, processed_html);
    }
}
<|endoftext|>


<commit_before>extern crate hyper;

use std::io::Write;

use hyper::Server;
use hyper::server::Request;
use hyper::server::Response;
use hyper::net::Fresh;
use hyper::uri::RequestUri;

fn hello(req: Request, res: Response<Fresh>) {
    let mut res = res.start().unwrap();

    let path = match req.uri {
      RequestUri::AbsolutePath(str) => str,
      _ => "".to_string()
    };

    println!("path: {}", path);
    println!("remoteAddy: {}", req.remote_addr);
    println!("method: {}", req.method);

    res.write_all(b"Hello").unwrap();
    res.end().unwrap();
}

fn main() {
    Server::http(hello).listen("127.0.0.1:3000").unwrap();
}
<commit_msg>Basic server running<commit_after>extern crate hyper;

use std::io::Write;

use hyper::Server;
use hyper::server::Request;
use hyper::server::Response;
use hyper::net::Fresh;
use hyper::uri::RequestUri;

fn hello(req: Request, res: Response<Fresh>) {
    let mut res = res.start().unwrap();

    let path = match req.uri {
      RequestUri::AbsolutePath(str) => str,
      _ => "".to_string()
    };

    println!("path: {}", path);
    println!("remoteAddy: {}", req.remote_addr);
    println!("method: {}", req.method);

    let compound = format!("{} request for {} from {}", req.method, path, req.remote_addr);

    res.write_all(compound.as_bytes()).unwrap();
    res.end().unwrap();
}

fn main() {
    Server::http(hello).listen("127.0.0.1:3000").unwrap();
}
<|endoftext|>


<commit_before>#![feature(phase)]
#![feature(if_let)]

#[phase(plugin, link)]
extern crate ecs;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;
use std::cell::RefCell;
use std::rc::Rc;
use std::rand;
use std::rand::Rng;
use std::num::abs;

use opengl_graphics::{
    Gl,
};
use sdl2_game_window::WindowSDL2;
use piston::{
    EventIterator,
    EventSettings,
    WindowSettings,
};
use piston::graphics::{
    AddBorder,
    AddRectangle,
    AddColor,
    Context,
    Draw,
};
use piston::event::{
    UpdateEvent,
    Event,
    Render,
    Update,
    Input,
};
use piston::input::{
    Focus,
};
use ecs::{
    Components,
    WorldBuilder,
    Entity,
    EntityData,
    EntityBuilder,
    Aspect
};
use ecs::system::entitysystem::{
    BulkEntitySystem,
    BulkEntityProcess,
};

struct DrawSystem {
    gl: RefCell<Gl>,
    event: Rc<RefCell<Event>>,
}

impl DrawSystem {
    fn new(event: Rc<RefCell<Event>>) -> DrawSystem {
        let opengl = piston::shader_version::opengl::OpenGL_3_2;
        DrawSystem {
            gl: RefCell::new(Gl::new(opengl)),
            event: event
        }
    }
}

impl BulkEntityProcess for DrawSystem {
  fn process(&self, es: Vec<&Entity>, data: &mut EntityData) {
    if let &Render(args) = self.event.borrow().deref() {
        let w = args.width as f64;
        let h = args.height as f64;
        let mut gl_cell = self.gl.borrow_mut();
        let gl = gl_cell.deref_mut();
        gl.viewport(0, 0, w as i32, h as i32);
        let c = Context::abs(w, h);
        // Clear background.
        c.rgb(0.0, 0.0, 0.0).draw(gl);

        let halfw = w / 2.0;
        let halfh = h / 2.0;
        for e in es.iter() {
          if let Some(ref mut position) = data.borrow::<Position>(*e) {
            let dx = halfw - position.x;
            let dy = halfh - position.y;
            let dx2 = dx * dx;
            let dy2 = dy * dy;
            let maxd2 = halfw * halfw + halfh * halfh;
            let d2 = dx2 + dy2;
            let rad = position.rad * (1.0 - d2 / maxd2);
            let ref mut rng = rand::task_rng();
            let l = rng.gen_range(0.8, 1.0);
            c.rgb(0.5 * l, 0.2 * l, 0.8 * l)
              .rect(position.x, position.y, rad, rad)
              .border_width(rng.gen_range(1.0, 3.0))
              .draw(gl);
          }
        }
    }
  }
}

struct MoveSystem {
    event: Rc<RefCell<Event>>
}

impl MoveSystem {
  fn new(event: Rc<RefCell<Event>>) -> MoveSystem {
    MoveSystem {
        event: event
    }
  }
}

impl BulkEntityProcess for MoveSystem {
  fn process(&self, es: Vec<&Entity>, data: &mut EntityData) {
    if let &Update(args) = self.event.borrow().deref() {
        let dt = args.dt;
        for e in es.iter() {
          let xv;
          let yv;
          if let Some(ref mut velocity) = data.borrow::<Velocity>(*e) {
              let ref mut rng = rand::task_rng();
              if rng.gen_range(0u, 100u) >= 99 {
                //velocity.y = -velocity.y;
              }
              if rng.gen_range(0u, 100u) >= 99 {
                //velocity.x = -velocity.x;
              }
              xv = velocity.x;
              yv = velocity.y;
          } else {
            return
          }
          if let Some(ref mut position) = data.borrow::<Position>(*e) {
            position.x += xv * dt;
            position.y += yv * dt;
          }
        }
    }
  }
}

component!(Position {
    x: f64,
    y: f64,
    rad: f64
})

component!(Velocity{
    x: f64,
    y: f64
})

fn main() {
    let ecs_event: Rc<RefCell<Event>> =
        Rc::new(
            RefCell::new(Input(Focus(false)))
        );

    let opengl = piston::shader_version::opengl::OpenGL_3_2;
    let mut window = WindowSDL2::new(
        opengl,
        WindowSettings {
            title: "Shooter".to_string(),
            size: [800, 600],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut world_builder = WorldBuilder::new();
    world_builder.register_component::<Position>();
    world_builder.register_component::<Velocity>();

    let aspect = Aspect::for_all(component_ids!(Position));
    let draw_sys = DrawSystem::new(ecs_event.clone());
    let sys = BulkEntitySystem::new(box draw_sys, aspect);
    world_builder.register_system(box sys);

    let maspect = Aspect::for_all(component_ids!(Position, Velocity));
    let move_sys = MoveSystem::new(ecs_event.clone());
    let bsys = BulkEntitySystem::new(box move_sys, maspect);
    world_builder.register_system(box bsys);

    let ref mut world = world_builder.build();

    let num_things: i32 = 80;

    let ref mut rng = rand::task_rng();
    for _ in range(0, num_things) {
        let r = 30.0;
        let x = (800.0 - r) / 2.0;
        let y = (600.0 - r) / 2.0;
        let d = Position { x: x, y: y, rad: r };
        let m = Velocity {
          x: rng.gen_range(-80.0, 80.0),
          y: rng.gen_range(-80.0, 80.0),
        };
        world.build_entity(|c: &mut Components, e: Entity|{
            c.add(&e, d);
            c.add(&e, m);
        });
    }
    let event_settings = EventSettings {
        updates_per_second: 120,
        max_frames_per_second: 60,
    };

    for e in EventIterator::new(&mut window, &event_settings) {
        *ecs_event.borrow_mut() = e.clone();
        world.update();
    }
}
<commit_msg>Remove silly code from systems<commit_after>#![feature(phase)]
#![feature(if_let)]

#[phase(plugin, link)]
extern crate ecs;
extern crate piston;
extern crate sdl2_game_window;
extern crate opengl_graphics;
use std::cell::RefCell;
use std::default::Default;
use std::rc::Rc;
use std::rand;
use std::rand::Rng;
use std::num::abs;

use opengl_graphics::{
    Gl,
};
use sdl2_game_window::WindowSDL2;
use piston::{
    EventIterator,
    EventSettings,
    WindowSettings,
};
use piston::graphics::{
    AddBorder,
    AddRectangle,
    AddEllipse,
    AddColor,
    Context,
    Draw,
};
use piston::event::{
    UpdateEvent,
    Event,
    Render,
    Update,
    Input,
};
use piston::input::{
    Focus,
};
use ecs::{
    Components,
    WorldBuilder,
    Entity,
    EntityData,
    EntityBuilder,
    Aspect
};
use ecs::system::entitysystem::{
    BulkEntitySystem,
    BulkEntityProcess,
};

struct DrawSystem {
    gl: RefCell<Gl>,
    event: Rc<RefCell<Event>>,
}

impl DrawSystem {
    fn new(event: Rc<RefCell<Event>>) -> DrawSystem {
        let opengl = piston::shader_version::opengl::OpenGL_3_2;
        DrawSystem {
            gl: RefCell::new(Gl::new(opengl)),
            event: event
        }
    }
}

impl BulkEntityProcess for DrawSystem {
  fn process(&self, es: Vec<&Entity>, data: &mut EntityData) {
    if let &Render(args) = self.event.borrow().deref() {
        let w = args.width as f64;
        let h = args.height as f64;
        let mut gl_cell = self.gl.borrow_mut();
        let gl = gl_cell.deref_mut();
        gl.viewport(0, 0, w as i32, h as i32);
        let c = Context::abs(w, h);
        // Clear background.
        c.rgb(0.0, 0.0, 0.0).draw(gl);

        for e in es.iter() {
          let mut x: f64 = 0.0;
          let mut y: f64 = 0.0;
          let mut shape = Point;
          let mut border = None;
          if let Some(p) = data.borrow::<PositionComponent>(*e) {
            x = p.x;
            y = p.y;
          }
          if let Some(s) = data.borrow::<ShapeComponent>(*e) {
            shape = s.shape;
            border = s.border;
          }
          let r = 0.5;
          let g = 0.2;
          let b = 0.8;
          match (shape, border) {
              (Point, Some(border)) => {
                  c.rgb(r, g, b)
                    .rect(x, y, 1.0, 1.0)
                    .border_radius(border)
                    .draw(gl);
              },
              (Circle(rad), Some(border)) => {
                  c.rgb(r, g, b)
                    .ellipse(x, y, rad, rad)
                    .border_radius(border)
                    .draw(gl);
              },
              (Square(w,h), Some(border)) => {
                  c.rgb(r, g, b)
                    .rect(x, y, w, h)
                    .border_radius(border)
                    .draw(gl);
              },
              (Point, None) => {
                  c.rgb(r, g, b)
                    .rect(x, y, 1.0, 1.0)
                    .draw(gl);
              },
              (Circle(rad), None) => {
                  c.rgb(r, g, b)
                    .ellipse(x, y, rad, rad)
                    .draw(gl);
              },
              (Square(w,h), None) => {
                  c.rgb(r, g, b)
                    .rect(x, y, w, h)
                    .draw(gl);
              }
          }
        }
    }
  }
}

struct MoveSystem {
    event: Rc<RefCell<Event>>
}

impl MoveSystem {
  fn new(event: Rc<RefCell<Event>>) -> MoveSystem {
    MoveSystem {
        event: event
    }
  }
}

impl BulkEntityProcess for MoveSystem {
  fn process(&self, es: Vec<&Entity>, data: &mut EntityData) {
    if let &Update(args) = self.event.borrow().deref() {
        let dt = args.dt;
        for e in es.iter() {
          let xv;
          let yv;
          if let Some(ref mut velocity) = data.borrow::<VelocityComponent>(*e) {
              xv = velocity.x;
              yv = velocity.y;
          } else {
            return
          }
          if let Some(ref mut position) = data.borrow::<PositionComponent>(*e) {
            position.x += xv * dt;
            position.y += yv * dt;
          }
        }
    }
  }
}

component!(PositionComponent {
    x: f64,
    y: f64
})

#[deriving(Clone, PartialEq, Show)]
pub enum Shape {
    Point,
    Circle(f64), // radius
    Square(f64, f64), // width, height
}

impl Default for Shape {
    fn default() -> Shape { Point }
}

component!(ShapeComponent {
    shape: Shape,
    border: Option<f64>
})

component!(VelocityComponent {
    x: f64,
    y: f64
})

fn main() {
    let ecs_event: Rc<RefCell<Event>> =
        Rc::new(
            RefCell::new(Input(Focus(false)))
        );

    let opengl = piston::shader_version::opengl::OpenGL_3_2;
    let mut window = WindowSDL2::new(
        opengl,
        WindowSettings {
            title: "Shooter".to_string(),
            size: [800, 600],
            fullscreen: false,
            exit_on_esc: true,
            samples: 4,
        }
    );

    let mut world_builder = WorldBuilder::new();
    world_builder.register_component::<PositionComponent>();
    world_builder.register_component::<VelocityComponent>();
    world_builder.register_component::<ShapeComponent>();

    let aspect = Aspect::for_all(component_ids!(PositionComponent));
    let draw_sys = DrawSystem::new(ecs_event.clone());
    let sys = BulkEntitySystem::new(box draw_sys, aspect);
    world_builder.register_system(box sys);

    let maspect = Aspect::for_all(component_ids!(PositionComponent, VelocityComponent));
    let move_sys = MoveSystem::new(ecs_event.clone());
    let bsys = BulkEntitySystem::new(box move_sys, maspect);
    world_builder.register_system(box bsys);

    let ref mut world = world_builder.build();

    let num_things: i32 = 300;

    let ref mut rng = rand::task_rng();
    for _ in range(0, num_things) {
        let r = 30.0;
        let x = (800.0 - r) / 2.0;
        let y = (600.0 - r) / 2.0;
        let d = PositionComponent { x: x, y: y };
        let m = VelocityComponent {
          x: rng.gen_range(-80.0, 80.0),
          y: rng.gen_range(-80.0, 80.0),
        };
        let shape = ShapeComponent {
          shape: if rng.gen() {
                     Square(r, r)
                 } else {
                     Circle(r)
                 },
          border: if rng.gen() {
                     Some(rng.gen_range(1.0, 5.0))
                 } else {
                     None
                 }
        };
        world.build_entity(|c: &mut Components, e: Entity| {
            c.add(&e, d);
            c.add(&e, m);
            c.add(&e, shape);
        });
    }
    let event_settings = EventSettings {
        updates_per_second: 120,
        max_frames_per_second: 60,
    };

    for e in EventIterator::new(&mut window, &event_settings) {
        *ecs_event.borrow_mut() = e.clone();
        world.update();
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

extern crate rustc_serialize;
extern crate docopt;

mod config;
mod project;
mod language;

use docopt::Docopt;
use project::{Project, ProjectError};
use std::process::exit;

static USAGE: &'static str = "
Intecture CLI.

Usage:
  incli [(-v | --verbose)] run [<arg>...]
  incli [(-v | --verbose)] init [(-b | --blank)] (<name> <lang>)
  incli (-h | --help)
  incli --version

Options:
  -h --help     Show this screen.
  --version     Show version.
  -v --verbose  Verbose output.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_run: bool,
    cmd_init: bool,
    flag_h: bool,
    flag_help: bool,
    flag_v: bool,
    flag_verbose: bool,
    flag_b: bool,
    flag_blank: bool,
    flag_version: bool,
    arg_arg: Vec<String>,
    arg_name: String,
    arg_lang: String,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let args: Args = Docopt::new(USAGE)
	    .and_then(|d| d.decode())
	    .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("0.0.1");
    } else if args.cmd_run == true {
        match Project::new() {
            Ok(project) => {
                let result = project.run(&args.arg_arg);

                if let Some(e) = result.err() {
                    println!("{:?}", e);
                    exit(1);
                }
            },
            Err(e) => print_err(&e, args.flag_v || args.flag_verbose),
        }
    } else if args.cmd_init == true {
        match Project::create(&args.arg_name, &args.arg_lang, args.flag_b || args.flag_blank) {
            Ok(_) => println!("Created project {}", args.arg_name),
            Err(e) => print_err(&e, args.flag_v || args.flag_verbose),
        }
    }
}

fn print_err(e: &ProjectError, verbose: bool) {
    println!("{}", e.message);

    if verbose {
        println!("{:?}", e.root);
    }

    exit(1);
}
<commit_msg>Corrected options output<commit_after>// Copyright 2015 Intecture Developers. See the COPYRIGHT file at the
// top-level directory of this distribution and at
// https://intecture.io/COPYRIGHT.
//
// Licensed under the Mozilla Public License 2.0 <LICENSE or
// https://www.tldrlegal.com/l/mpl-2.0>. This file may not be copied,
// modified, or distributed except according to those terms.

extern crate rustc_serialize;
extern crate docopt;

mod config;
mod project;
mod language;

use docopt::Docopt;
use project::{Project, ProjectError};
use std::process::exit;

static USAGE: &'static str = "
Intecture CLI.

Usage:
  incli [(-v | --verbose)] run [<arg>...]
  incli [(-v | --verbose)] init [(-b | --blank)] (<name> <lang>)
  incli (-h | --help)
  incli --version

Options:
  -b --blank	Create a blank project.
  -h --help     Show this screen.
  -v --verbose  Verbose output.
  --version     Show version.
";

#[derive(Debug, RustcDecodable)]
struct Args {
    cmd_run: bool,
    cmd_init: bool,
    flag_h: bool,
    flag_help: bool,
    flag_v: bool,
    flag_verbose: bool,
    flag_b: bool,
    flag_blank: bool,
    flag_version: bool,
    arg_arg: Vec<String>,
    arg_name: String,
    arg_lang: String,
}

#[cfg_attr(test, allow(dead_code))]
fn main() {
    let args: Args = Docopt::new(USAGE)
	    .and_then(|d| d.decode())
	    .unwrap_or_else(|e| e.exit());

    if args.flag_version {
        println!("0.0.1");
    } else if args.cmd_run == true {
        match Project::new() {
            Ok(project) => {
                let result = project.run(&args.arg_arg);

                if let Some(e) = result.err() {
                    println!("{:?}", e);
                    exit(1);
                }
            },
            Err(e) => print_err(&e, args.flag_v || args.flag_verbose),
        }
    } else if args.cmd_init == true {
        match Project::create(&args.arg_name, &args.arg_lang, args.flag_b || args.flag_blank) {
            Ok(_) => println!("Created project {}", args.arg_name),
            Err(e) => print_err(&e, args.flag_v || args.flag_verbose),
        }
    }
}

fn print_err(e: &ProjectError, verbose: bool) {
    println!("{}", e.message);

    if verbose {
        println!("{:?}", e.root);
    }

    exit(1);
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

mod lib;
use lib::Petnames;

use std::collections::HashSet;
use std::fs;
use std::io;
use std::path;
use std::process;
use std::str::FromStr;

use clap::{App, Arg};
use rand::seq::IteratorRandom;

fn main() {
    if let Err(e) = run() {
        eprintln!("Error: {}", e);
        process::exit(1);
    }
}

fn run() -> io::Result<()> {
    let matches = App::new("rust-petname")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generate human readable random names.")
        .after_help(concat!(
            "Based on Dustin Kirkland's petname project ",
            "<https://github.com/dustinkirkland/petname>."
        ))
        .arg(
            Arg::with_name("words")
                .short("w")
                .long("words")
                .value_name("WORDS")
                .default_value("2")
                .help("Number of words in name")
                .takes_value(true)
                .validator(can_be_parsed::<u8>),
        )
        .arg(
            Arg::with_name("separator")
                .short("s")
                .long("separator")
                .value_name("SEP")
                .default_value("-")
                .help("Separator between words")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("complexity")
                .short("c")
                .long("complexity")
                .value_name("COM")
                .possible_values(&["0", "1", "2"])
                .hide_possible_values(true)
                .default_value("0")
                .help("Use small words (0), medium words (1), or large words (2)")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("directory")
                .short("d")
                .long("dir")
                .value_name("DIR")
                .help("Directory containing adjectives.txt, adverbs.txt, names.txt")
                .conflicts_with("complexity")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("count")
                .long("count")
                .value_name("COUNT")
                .default_value("1")
                .help("Generate multiple names; pass 0 to produce infinite names!")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("letters")
                .short("l")
                .long("letters")
                .value_name("LETTERS")
                .default_value("0")
                .help("Maxiumum number of letters in each word; 0 for unlimited")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("alliterate")
                .short("a")
                .long("alliterate")
                .help("Generate names where each word begins with the same letter")
                .takes_value(false),
        )
        .arg(
            // For compatibility with upstream.
            Arg::with_name("ubuntu")
                .short("u")
                .long("ubuntu")
                .help("Alias; see --alliterate")
                .takes_value(false),
        )
        .get_matches();

    // Unwrapping is safe because these options have defaults.
    let opt_separator = matches.value_of("separator").unwrap();
    let opt_words = matches.value_of("words").unwrap();
    let opt_complexity = matches.value_of("complexity").unwrap();
    let opt_count = matches.value_of("count").unwrap();
    let opt_letters = matches.value_of("letters").unwrap();
    let opt_alliterate = matches.is_present("alliterate") || matches.is_present("ubuntu");

    // Optional arguments without defaults.
    let opt_directory = matches.value_of("directory");

    // Parse numbers. Validated so unwrapping is okay.
    let opt_words: u8 = opt_words.parse().unwrap();
    let opt_count: usize = opt_count.parse().unwrap();
    let opt_letters: usize = opt_letters.parse().unwrap();

    // Load custom word lists, if specified.
    let words = match opt_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 opt_complexity {
            "0" => Petnames::small(),
            "1" => Petnames::medium(),
            "2" => Petnames::large(),
            _ => Petnames::small(),
        },
    };

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

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

    // If requested, choose a random letter then discard all words that do not
    // begin with that letter.
    if opt_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]);
        // Choose the first letter at random; fails if there are no letters.
        match firsts.iter().choose(&mut rng) {
            Some(c) => petnames.retain(|s| s.chars().next() == Some(*c)),
            None => panic!("no letters in common"), // TODO: do this without a panic.
        };
    }

    // Stream if count is 0.
    if opt_count == 0 {
        loop {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    } else {
        for _ in 1..=opt_count {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    }

    Ok(())
}

fn can_be_parsed<INTO>(value: String) -> Result<(), String>
where
    INTO: FromStr,
    <INTO as FromStr>::Err: std::fmt::Display,
{
    match value.parse::<INTO>() {
        Err(e) => Err(format!("{}", e)),
        Ok(_) => 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) -> io::Result<Self> {
        let dirname = dirname.as_ref();
        Ok(Self::Custom(
            fs::read_to_string(dirname.join("adjectives.txt"))?,
            fs::read_to_string(dirname.join("adverbs.txt"))?,
            fs::read_to_string(dirname.join("names.txt"))?,
        ))
    }
}
<commit_msg>Print more useful (and prettier) error messages.<commit_after>#[macro_use]
extern crate clap;

mod lib;
use lib::Petnames;

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

use clap::{App, Arg};
use rand::seq::IteratorRandom;

fn main() {
    if let Err(e) = run() {
        eprintln!("Error: {}", e);
        process::exit(1);
    }
}

enum Error {
    IoError(io::Error),
    FileIoError(path::PathBuf, io::Error),
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
        match *self {
            Error::IoError(ref e) => write!(f, "{}", e),
            Error::FileIoError(ref path, ref e) => write!(f, "{}: {}", e, path.display()),
        }
    }
}

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

fn run() -> Result<(), Error> {
    let matches = App::new("rust-petname")
        .version(crate_version!())
        .author(crate_authors!())
        .about("Generate human readable random names.")
        .after_help(concat!(
            "Based on Dustin Kirkland's petname project ",
            "<https://github.com/dustinkirkland/petname>."
        ))
        .arg(
            Arg::with_name("words")
                .short("w")
                .long("words")
                .value_name("WORDS")
                .default_value("2")
                .help("Number of words in name")
                .takes_value(true)
                .validator(can_be_parsed::<u8>),
        )
        .arg(
            Arg::with_name("separator")
                .short("s")
                .long("separator")
                .value_name("SEP")
                .default_value("-")
                .help("Separator between words")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("complexity")
                .short("c")
                .long("complexity")
                .value_name("COM")
                .possible_values(&["0", "1", "2"])
                .hide_possible_values(true)
                .default_value("0")
                .help("Use small words (0), medium words (1), or large words (2)")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("directory")
                .short("d")
                .long("dir")
                .value_name("DIR")
                .help("Directory containing adjectives.txt, adverbs.txt, names.txt")
                .conflicts_with("complexity")
                .takes_value(true),
        )
        .arg(
            Arg::with_name("count")
                .long("count")
                .value_name("COUNT")
                .default_value("1")
                .help("Generate multiple names; pass 0 to produce infinite names!")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("letters")
                .short("l")
                .long("letters")
                .value_name("LETTERS")
                .default_value("0")
                .help("Maxiumum number of letters in each word; 0 for unlimited")
                .takes_value(true)
                .validator(can_be_parsed::<usize>),
        )
        .arg(
            Arg::with_name("alliterate")
                .short("a")
                .long("alliterate")
                .help("Generate names where each word begins with the same letter")
                .takes_value(false),
        )
        .arg(
            // For compatibility with upstream.
            Arg::with_name("ubuntu")
                .short("u")
                .long("ubuntu")
                .help("Alias; see --alliterate")
                .takes_value(false),
        )
        .get_matches();

    // Unwrapping is safe because these options have defaults.
    let opt_separator = matches.value_of("separator").unwrap();
    let opt_words = matches.value_of("words").unwrap();
    let opt_complexity = matches.value_of("complexity").unwrap();
    let opt_count = matches.value_of("count").unwrap();
    let opt_letters = matches.value_of("letters").unwrap();
    let opt_alliterate = matches.is_present("alliterate") || matches.is_present("ubuntu");

    // Optional arguments without defaults.
    let opt_directory = matches.value_of("directory");

    // Parse numbers. Validated so unwrapping is okay.
    let opt_words: u8 = opt_words.parse().unwrap();
    let opt_count: usize = opt_count.parse().unwrap();
    let opt_letters: usize = opt_letters.parse().unwrap();

    // Load custom word lists, if specified.
    let words = match opt_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 opt_complexity {
            "0" => Petnames::small(),
            "1" => Petnames::medium(),
            "2" => Petnames::large(),
            _ => Petnames::small(),
        },
    };

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

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

    // If requested, choose a random letter then discard all words that do not
    // begin with that letter.
    if opt_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]);
        // Choose the first letter at random; fails if there are no letters.
        match firsts.iter().choose(&mut rng) {
            Some(c) => petnames.retain(|s| s.chars().next() == Some(*c)),
            None => panic!("no letters in common"), // TODO: do this without a panic.
        };
    }

    // Stream if count is 0.
    if opt_count == 0 {
        loop {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    } else {
        for _ in 1..=opt_count {
            let petname = petnames.generate(&mut rng, opt_words, opt_separator);
            println!("{}", petname);
        }
    }

    Ok(())
}

fn can_be_parsed<INTO>(value: String) -> Result<(), String>
where
    INTO: FromStr,
    <INTO as FromStr>::Err: std::fmt::Display,
{
    match value.parse::<INTO>() {
        Err(e) => Err(format!("{}", e)),
        Ok(_) => 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::FileIoError(path.as_ref().to_path_buf(), error))
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate fuse;
extern crate libc;
#[macro_use]
extern crate log;
extern crate chan_signal;

use std::error::Error;
use std::ffi::OsStr;
use std::path::Path;
use std::str::FromStr;
use std::thread;

use chan_signal::Signal;
use clap::{App, Arg};

mod catfs;
mod flags;
mod evicter;

use catfs::error;
use catfs::flags::{DiskSpace, FlagStorage};
use catfs::rlibc;

fn main() {
    if let Err(e) = main_internal() {
        error!("Cannot mount: {}", e);
        std::process::exit(1);
    }
}

fn main_internal() -> error::Result<()> {
    env_logger::init().unwrap();

    let mut flags: FlagStorage = Default::default();
    let mut test = false;

    let app = App::new("catfs")
        .about("Cache Anything FileSystem")
        .version(crate_version!());

    {
        fn diskspace_validator(s: String) -> Result<(), String> {
            DiskSpace::from_str(&s).map(|_| ()).map_err(
                |e| e.to_str().to_owned(),
            )
        }

        fn path_validator(s: String) -> Result<(), String> {
            Path::new(&s)
                .canonicalize()
                .map_err(|e| e.description().to_owned())
                .and_then(|p| if p.is_dir() {
                    Ok(())
                } else {
                    Err("is not a directory".to_owned())
                })
        }

        let mut args = [
            flags::Flag {
                arg: Arg::with_name("space")
                    .long("free")
                    .takes_value(true)
                    .help(
                        "Ensure filesystem has at least this much free space. (ex: 9.5%, 10G)",
                    )
                    .validator(diskspace_validator),
                value: &mut flags.free_space,
            },
            flags::Flag {
                arg: Arg::with_name("foreground").short("f").help(
                    "Run catfs in foreground.",
                ),
                value: &mut flags.foreground,
            },
            flags::Flag {
                arg: Arg::with_name("option").short("o").takes_value(true).help(
                    "Additional system-specific mount options. Be careful!",
                ),
                value: &mut flags.mount_options,
            },
            flags::Flag {
                arg: Arg::with_name("test").long("test").help(
                    "Exit after parsing arguments",
                ),
                value: &mut test,
            },
            flags::Flag {
                arg: Arg::with_name("from")
                    .index(1)
                    .required(true)
                    .help("Cache files from this directory.")
                    .validator(path_validator),
                value: &mut flags.cat_from,
            },
            flags::Flag {
                arg: Arg::with_name("to")
                    .index(2)
                    .required(true)
                    .help("Cache files to this directory.")
                    .validator(path_validator),
                value: &mut flags.cat_to,
            },
            flags::Flag {
                arg: Arg::with_name("mountpoint")
                    .index(3)
                    .required(true)
                    .help("Expose the mount point at this directory.")
                    .validator(path_validator),
                value: &mut flags.mount_point,
            },
        ];

        flags::parse_options(app, &mut args);
    }

    if test {
        return Ok(());
    }

    let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]);
    let path_from = Path::new(&flags.cat_from).canonicalize()?;
    let path_to = Path::new(&flags.cat_to).canonicalize()?;
    let fs = catfs::CatFS::new(&path_from, &path_to)?;
    let cache_dir = fs.get_cache_dir()?;
    let mut options: Vec<&OsStr> = Vec::new();
    for i in 0..flags.mount_options.len() {
        options.push(&flags.mount_options[i]);
    }

    {
        let mut session = fuse::Session::new(fs, Path::new(&flags.mount_point), &options)?;
        unsafe {
            thread::spawn(move || {
                if let Err(e) = session.run() {
                    error!("session.run() = {}", e);
                }
                libc::kill(0, libc::SIGTERM);
            });
        }
        let mut ev = evicter::Evicter::new(cache_dir, &flags.free_space);
        ev.run();
        // unmount after we get signaled becausep session will go out of scope
        signal.recv().unwrap();
    }
    rlibc::close(cache_dir)?;
    return Ok(());
}
<commit_msg>log with timestamp<commit_after>extern crate chan_signal;
#[macro_use]
extern crate clap;
extern crate env_logger;
extern crate fuse;
extern crate libc;
#[macro_use]
extern crate log;
extern crate time;

use std::env;
use std::error::Error;
use std::ffi::OsStr;
use std::os::unix::ffi::OsStrExt;
use std::io;
use std::path::Path;
use std::str::FromStr;
use std::thread;

use chan_signal::Signal;
use clap::{App, Arg};
use env_logger::LogBuilder;
use log::LogRecord;

mod catfs;
mod flags;
mod evicter;

use catfs::error;
use catfs::flags::{DiskSpace, FlagStorage};
use catfs::rlibc;

fn main() {
    if let Err(e) = main_internal() {
        error!("Cannot mount: {}", e);
        std::process::exit(1);
    }
}

fn main_internal() -> error::Result<()> {
    let format = |record: &LogRecord| {
        let t = time::now();
        format!(
            "{} {:5} - {}",
            time::strftime("%Y-%m-%d %H:%M:%S", &t).unwrap(),
            record.level(),
            record.args()
        )
    };

    let mut builder = LogBuilder::new();
    builder.format(format);

    if env::var("RUST_LOG").is_ok() {
        builder.parse(&env::var("RUST_LOG").unwrap());
    }

    builder.init().unwrap();

    let mut flags: FlagStorage = Default::default();
    let mut test = false;

    let app = App::new("catfs")
        .about("Cache Anything FileSystem")
        .version(crate_version!());

    {
        fn diskspace_validator(s: String) -> Result<(), String> {
            DiskSpace::from_str(&s).map(|_| ()).map_err(
                |e| e.to_str().to_owned(),
            )
        }

        fn path_validator(s: String) -> Result<(), String> {
            Path::new(&s)
                .canonicalize()
                .map_err(|e| e.description().to_owned())
                .and_then(|p| if p.is_dir() {
                    Ok(())
                } else {
                    Err("is not a directory".to_owned())
                })
        }

        let mut args = [
            flags::Flag {
                arg: Arg::with_name("space")
                    .long("free")
                    .takes_value(true)
                    .help(
                        "Ensure filesystem has at least this much free space. (ex: 9.5%, 10G)",
                    )
                    .validator(diskspace_validator),
                value: &mut flags.free_space,
            },
            flags::Flag {
                arg: Arg::with_name("foreground").short("f").help(
                    "Run catfs in foreground.",
                ),
                value: &mut flags.foreground,
            },
            flags::Flag {
                arg: Arg::with_name("option").short("o").takes_value(true).help(
                    "Additional system-specific mount options. Be careful!",
                ),
                value: &mut flags.mount_options,
            },
            flags::Flag {
                arg: Arg::with_name("test").long("test").help(
                    "Exit after parsing arguments",
                ),
                value: &mut test,
            },
            flags::Flag {
                arg: Arg::with_name("from")
                    .index(1)
                    .required(true)
                    .help("Cache files from this directory.")
                    .validator(path_validator),
                value: &mut flags.cat_from,
            },
            flags::Flag {
                arg: Arg::with_name("to")
                    .index(2)
                    .required(true)
                    .help("Cache files to this directory.")
                    .validator(path_validator),
                value: &mut flags.cat_to,
            },
            flags::Flag {
                arg: Arg::with_name("mountpoint")
                    .index(3)
                    .required(true)
                    .help("Expose the mount point at this directory.")
                    .validator(path_validator),
                value: &mut flags.mount_point,
            },
        ];

        flags::parse_options(app, &mut args);
    }

    if test {
        return Ok(());
    }

    let signal = chan_signal::notify(&[Signal::INT, Signal::TERM]);
    let path_from = Path::new(&flags.cat_from).canonicalize()?;
    let path_to = Path::new(&flags.cat_to).canonicalize()?;
    let fs = catfs::CatFS::new(&path_from, &path_to)?;
    let cache_dir = fs.get_cache_dir()?;
    let mut options: Vec<&OsStr> = Vec::new();
    for i in 0..flags.mount_options.len() {
        options.push(&flags.mount_options[i]);
    }

    {
        let mut session = fuse::Session::new(fs, Path::new(&flags.mount_point), &options)?;
        unsafe {
            thread::spawn(move || {
                if let Err(e) = session.run() {
                    error!("session.run() = {}", e);
                }
                libc::kill(0, libc::SIGTERM);
            });
        }
        let mut ev = evicter::Evicter::new(cache_dir, &flags.free_space);
        ev.run();
        // unmount after we get signaled becausep session will go out of scope
        signal.recv().unwrap();
    }
    rlibc::close(cache_dir)?;
    return Ok(());
}
<|endoftext|>


<commit_before>//! Assorted mathematical helper functions.

use conv::{
    ValueInto
};
use num::{
    Zero
};
pub use nalgebra::{
    Mat2,
    Vec2
};
use nalgebra::{
    Eye,
    inv,
    Inv
};

pub fn l2_norm(xs: &[f32]) -> f32 {
    xs.iter().fold(0f32, |acc, x| acc + x * x).sqrt()
}

/// Helper for a conversion that we know can't fail.
pub fn cast<T, U>(x: T) -> U where T: ValueInto<U> {
    match x.value_into() {
        Ok(y) => y,
        Err(_) => panic!("Failed to convert"),
    }
}

/// A 2d affine transformation.
// TODO: Should we switch to homogeneous coordinates?
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Affine2 {
    pub linear: Mat2<f32>,
    pub translation: Vec2<f32>
}

impl Affine2 {

    /// Constructs an Affine2 with given linear transformation
    /// and translation.
    pub fn new(linear: Mat2<f32>, translation: Vec2<f32>) -> Affine2 {
        Affine2 { linear: linear, translation: translation }
    }

    /// The identity transformation.
    pub fn identity() -> Affine2 {
        Affine2 { linear: Mat2::new_identity(2), translation: Zero::zero() }
    }

    /// Returns the inverse of an affine transformation, or
    /// None if its linear part is singular.
    pub fn inverse(&self) -> Option<Affine2> {
        if let Some(inv) = self.linear.inv() {
            Some(Affine2 {
                linear: inv,
                translation: Vec2::new(0f32, 0f32) - inv * self.translation })
        }
        else {
            None
        }
    }

    /// Applies the affine transformation to a given vector.
    pub fn apply(&self, x: Vec2<f32>) -> Vec2<f32> {
        self.linear * x + self.translation
    }
}

#[cfg(test)]
mod test {

    use super::{
        Affine2
    };

    use nalgebra::{
        Mat2,
        Vec2
    };

    #[test]
    fn test_affine2_apply() {
        let aff = Affine2::new(
            Mat2::new(1f32, 2f32, 3f32, 4f32),
            Vec2::new(0f32, 1f32));
        let x = Vec2::new(1f32, 2f32);
        assert_eq!(aff.apply(x), Vec2::new(5f32, 12f32));
    }

    #[test]
    fn test_affine2_inverse_singular() {
        let aff = Affine2::new(
            Mat2::new(0f32, 1f32, 0f32, 1f32),
            Vec2::new(0f32, 1f32));
        assert_eq!(aff.inverse(), None);
    }

    #[test]
    fn test_affine2_inverse_nonsingular() {
        let mat = Mat2::new(1f32, 2f32, 3f32, 4f32);
        let aff = Affine2::new(mat, Vec2::new(7f32, -4f32));

        if let Some(inv) = aff.inverse() {
            let test = Vec2::new(4.2f32, 8.7f32);
            let actual = inv.apply(aff.apply(test));
            assert!((test[0] - actual[0]).abs() <= 1e-4,
                format!("left: {:?}, right: {:?}", test[0], actual[0]));
            assert!((test[1] - actual[1]).abs() <= 1e-4,
                format!("left: {:?}, right: {:?}", test[1], actual[1]));
        }
        else {
            assert!(false, "No inverse returned");
        }
    }
}
<commit_msg>Add comment<commit_after>//! Assorted mathematical helper functions.

use conv::{
    ValueInto
};
use num::{
    Zero
};
pub use nalgebra::{
    Mat2,
    Vec2
};
use nalgebra::{
    Eye,
    inv,
    Inv
};

// L2 norm of a vector.
pub fn l2_norm(xs: &[f32]) -> f32 {
    xs.iter().fold(0f32, |acc, x| acc + x * x).sqrt()
}

/// Helper for a conversion that we know can't fail.
pub fn cast<T, U>(x: T) -> U where T: ValueInto<U> {
    match x.value_into() {
        Ok(y) => y,
        Err(_) => panic!("Failed to convert"),
    }
}

/// A 2d affine transformation.
// TODO: Should we switch to homogeneous coordinates?
#[derive(Copy, Clone, PartialEq, Debug)]
pub struct Affine2 {
    pub linear: Mat2<f32>,
    pub translation: Vec2<f32>
}

impl Affine2 {

    /// Constructs an Affine2 with given linear transformation
    /// and translation.
    pub fn new(linear: Mat2<f32>, translation: Vec2<f32>) -> Affine2 {
        Affine2 { linear: linear, translation: translation }
    }

    /// The identity transformation.
    pub fn identity() -> Affine2 {
        Affine2 { linear: Mat2::new_identity(2), translation: Zero::zero() }
    }

    /// Returns the inverse of an affine transformation, or
    /// None if its linear part is singular.
    pub fn inverse(&self) -> Option<Affine2> {
        if let Some(inv) = self.linear.inv() {
            Some(Affine2 {
                linear: inv,
                translation: Vec2::new(0f32, 0f32) - inv * self.translation })
        }
        else {
            None
        }
    }

    /// Applies the affine transformation to a given vector.
    pub fn apply(&self, x: Vec2<f32>) -> Vec2<f32> {
        self.linear * x + self.translation
    }
}

#[cfg(test)]
mod test {

    use super::{
        Affine2
    };

    use nalgebra::{
        Mat2,
        Vec2
    };

    #[test]
    fn test_affine2_apply() {
        let aff = Affine2::new(
            Mat2::new(1f32, 2f32, 3f32, 4f32),
            Vec2::new(0f32, 1f32));
        let x = Vec2::new(1f32, 2f32);
        assert_eq!(aff.apply(x), Vec2::new(5f32, 12f32));
    }

    #[test]
    fn test_affine2_inverse_singular() {
        let aff = Affine2::new(
            Mat2::new(0f32, 1f32, 0f32, 1f32),
            Vec2::new(0f32, 1f32));
        assert_eq!(aff.inverse(), None);
    }

    #[test]
    fn test_affine2_inverse_nonsingular() {
        let mat = Mat2::new(1f32, 2f32, 3f32, 4f32);
        let aff = Affine2::new(mat, Vec2::new(7f32, -4f32));

        if let Some(inv) = aff.inverse() {
            let test = Vec2::new(4.2f32, 8.7f32);
            let actual = inv.apply(aff.apply(test));
            assert!((test[0] - actual[0]).abs() <= 1e-4,
                format!("left: {:?}, right: {:?}", test[0], actual[0]));
            assert!((test[1] - actual[1]).abs() <= 1e-4,
                format!("left: {:?}, right: {:?}", test[1], actual[1]));
        }
        else {
            assert!(false, "No inverse returned");
        }
    }
}
<|endoftext|>