<commit_before>use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

use anyhow::Context;
use once_cell::sync::OnceCell;
use reqwest;
use structopt::StructOpt;
use tbot;
use tokio;

mod data;
mod feed;

use crate::data::Database;

static BOT_NAME: OnceCell<String> = OnceCell::new();

#[derive(Debug, StructOpt)]
#[structopt(about = "A simple Telegram RSS bot.")]
struct Opt {
    /// Telegram bot token
    token: String,
    /// Path to database
    #[structopt(short = "d", default_value = "./rssbot.json")]
    database: PathBuf,
}

macro_rules! handle {
    ($env: expr, $f: expr) => {{
        let env = $env.clone();
        let f = $f;
        move |cmd| {
            let future = f(env.clone(), cmd);
            async {
                if let Err(e) = future.await {
                    dbg!(e);
                }
            }
        }
    }};
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let opt = Opt::from_args();
    let db = Arc::new(Mutex::new(Database::open(opt.database)?));
    let bot = tbot::Bot::new(opt.token);
    let me = bot
        .get_me()
        .call()
        .await
        .context("Initialization failed, check your network and Telegram token")?;

    BOT_NAME.set(me.user.username.clone().unwrap()).unwrap();

    let mut event_loop = bot.event_loop();
    event_loop.username(me.user.username.unwrap());
    event_loop.command("rss", handle!(db, handlers::rss));
    event_loop.command("sub", handle!(db, handlers::sub));

    event_loop.polling().start().await.unwrap();
    Ok(())
}

mod handlers {
    use std::future::Future;
    use std::pin::Pin;
    use std::sync::Mutex;
    use std::sync::{Arc, Once};
    use std::time::Duration;

    use reqwest;
    use tbot::{
        connectors::Https,
        contexts::{Command, Text},
        types::parameters,
    };

    use crate::data::{DataError, Database};
    use crate::feed::RSS;

    pub const TELEGRAM_MAX_MSG_LEN: usize = 4096;
    const RESP_SIZE_LIMIT: usize = 2 * 1024 * 1024;

    fn client() -> Arc<reqwest::Client> {
        static mut CLIENT: Option<Arc<reqwest::Client>> = None;
        static INIT: Once = Once::new();

        INIT.call_once(|| {
            let mut headers = reqwest::header::HeaderMap::new();
            let ua = format!(
                concat!(
                    env!("CARGO_PKG_NAME"),
                    "/",
                    env!("CARGO_PKG_VERSION"),
                    " (+https://t.me/{})"
                ),
                crate::BOT_NAME.get().expect("BOT_NAME not initialized")
            );
            headers.insert(
                reqwest::header::USER_AGENT,
                reqwest::header::HeaderValue::from_str(&ua).unwrap(),
            );
            let client = reqwest::Client::builder()
                .timeout(Duration::from_secs(5))
                .default_headers(headers)
                .redirect(reqwest::redirect::Policy::limited(5))
                .build()
                .unwrap();

            unsafe {
                CLIENT = Some(Arc::new(client));
            }
        });

        unsafe { CLIENT.clone() }.unwrap()
    }

    pub async fn rss(
        db: Arc<Mutex<Database>>,
        cmd: Arc<Command<Text<Https>>>,
    ) -> anyhow::Result<()> {
        let chat = cmd.chat.id;
        let text = &cmd.text.value;

        let feeds = db.lock().unwrap().subscribed_feeds(chat.0);
        let msgs = if let Some(feeds) = feeds {
            format_and_split_msgs("订阅列表：".to_string(), &feeds, |feed| {
                format!(
                    "{}",
                    Escape(&feed.link),
                    Escape(&feed.title)
                )
            })
        } else {
            vec!["订阅列表为空".to_string()]
        };

        let mut prev_msg = cmd.message_id;
        for msg in msgs {
            let text = parameters::Text::html(&msg);
            let msg = cmd.bot
                .send_message(chat, text)
                .reply_to_message_id(prev_msg)
                .call()
                .await?;
            prev_msg = msg.id;
        }
        Ok(())
    }

    pub async fn sub(
        db: Arc<Mutex<Database>>,
        cmd: Arc<Command<Text<Https>>>,
    ) -> anyhow::Result<()> {
        let chat_id = cmd.chat.id;
        let text = &cmd.text.value;
        let args = text.split_whitespace().collect::<Vec<_>>();
        let mut target_id = chat_id;
        let feed_url;

        match &*args {
            [url] => feed_url = url,
            [channel, url] => {
                //...
                target_id = todo!();
                feed_url = url;
            }
            [..] => {
                return Ok(());
            }
        };
        let msg = match pull_feed(feed_url).await {
            Ok(feed) => match db.lock().unwrap().subscribe(target_id.0, feed_url, &feed) {
                Ok(()) => format!("{} 订阅成功", feed.title),
                Err(DataError::Subscribed) => "".into(),
                Err(e) => unreachable!(e),
            },
            Err(e) => format!("订阅失败 {}", e),
        };
        cmd.bot
            .send_message(chat_id, text)
            .reply_to_message_id(cmd.message_id)
            .call()
            .await?;
        Ok(())
    }

    async fn pull_feed(url: &str) -> anyhow::Result<RSS> {
        let mut resp = client().get(url).send().await?.error_for_status()?;
        if let Some(len) = resp.content_length() {
            if len > RESP_SIZE_LIMIT as u64 {
                return Err(anyhow::format_err!("too big"));
            }
        }
        let mut buf = Vec::new(); // TODO: capacity?
        while let Some(bytes) = resp.chunk().await? {
            if buf.len() + bytes.len() > RESP_SIZE_LIMIT {
                return Err(anyhow::format_err!("too big"));
            }
            buf.extend_from_slice(&bytes);
        }

        crate::feed::parse(std::io::Cursor::new(buf))
    }

    pub fn format_and_split_msgs<T, F>(head: String, data: &[T], line_format_fn: F) -> Vec<String>
    where
        F: Fn(&T) -> String,
    {
        let mut msgs = vec![head];
        for item in data {
            let line = line_format_fn(item);
            if msgs.last_mut().unwrap().len() + line.len() > TELEGRAM_MAX_MSG_LEN {
                msgs.push(line);
            } else {
                let msg = msgs.last_mut().unwrap();
                msg.push('\n');
                msg.push_str(&line);
            }
        }
        msgs
    }

    pub struct Escape<'a>(pub &'a str);

    impl<'a> ::std::fmt::Display for Escape<'a> {
        fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
            // https://core.telegram.org/bots/api#html-style
            let Escape(s) = *self;
            let pile_o_bits = s;
            let mut last = 0;
            for (i, ch) in s.bytes().enumerate() {
                match ch as char {
                    '<' | '>' | '&' | '"' => {
                        fmt.write_str(&pile_o_bits[last..i])?;
                        let s = match ch as char {
                            '>' => ">",
                            '<' => "<",
                            '&' => "&",
                            '"' => """,
                            _ => unreachable!(),
                        };
                        fmt.write_str(s)?;
                        last = i + 1;
                    }
                    _ => {}
                }
            }

            if last < s.len() {
                fmt.write_str(&pile_o_bits[last..])?;
            }
            Ok(())
        }
    }
}
<commit_msg>Add optional channel argument<commit_after>#![feature(backtrace)]

use std::cell::RefCell;
use std::path::PathBuf;
use std::rc::Rc;
use std::sync::{Arc, Mutex};

use anyhow::Context;
use once_cell::sync::OnceCell;
use reqwest;
use structopt::StructOpt;
use tbot;
use tokio;

mod data;
mod feed;

use crate::data::Database;

static BOT_NAME: OnceCell<String> = OnceCell::new();
static BOT_ID: OnceCell<tbot::types::user::Id> = OnceCell::new();

#[derive(Debug, StructOpt)]
#[structopt(about = "A simple Telegram RSS bot.")]
struct Opt {
    /// Telegram bot token
    token: String,
    /// Path to database
    #[structopt(short = "d", default_value = "./rssbot.json")]
    database: PathBuf,
}

macro_rules! handle {
    ($env: expr, $f: expr) => {{
        let env = $env.clone();
        let f = $f;
        move |cmd| {
            let future = f(env.clone(), cmd);
            async {
                if let Err(e) = future.await {
                    dbg!(&e);
                    dbg!(e.backtrace());
                }
            }
        }
    }};
}

#[tokio::main]
async fn main() -> anyhow::Result<()> {
    let opt = Opt::from_args();
    let db = Arc::new(Mutex::new(Database::open(opt.database)?));
    let bot = tbot::Bot::new(opt.token);
    let me = bot
        .get_me()
        .call()
        .await
        .context("Initialization failed, check your network and Telegram token")?;

    BOT_NAME.set(me.user.username.clone().unwrap()).unwrap();
    BOT_ID.set(me.user.id).unwrap();

    let mut event_loop = bot.event_loop();
    event_loop.username(me.user.username.unwrap());
    event_loop.command("rss", handle!(db, handlers::rss));
    event_loop.command("sub", handle!(db, handlers::sub));

    event_loop.polling().start().await.unwrap();
    Ok(())
}

mod handlers {
    use std::future::Future;
    use std::pin::Pin;
    use std::sync::Mutex;
    use std::sync::{Arc, Once};
    use std::time::Duration;

    use reqwest;
    use tbot::{
        connectors::Https,
        contexts::{Command, Text},
        types::parameters,
    };

    use crate::data::{DataError, Database};
    use crate::feed::RSS;

    pub const TELEGRAM_MAX_MSG_LEN: usize = 4096;
    const RESP_SIZE_LIMIT: usize = 2 * 1024 * 1024;

    #[derive(Debug, Copy, Clone)]
    struct MsgTarget {
        chat_id: tbot::types::chat::Id,
        message_id: tbot::types::message::Id,
        first_time: bool,
    }

    impl MsgTarget {
        fn new(chat_id: tbot::types::chat::Id, message_id: tbot::types::message::Id) -> Self {
            MsgTarget {
                chat_id,
                message_id,
                first_time: false,
            }
        }
        fn update(self, message_id: tbot::types::message::Id) -> Self {
            MsgTarget { message_id, ..self }
        }
    }

    fn client() -> Arc<reqwest::Client> {
        static mut CLIENT: Option<Arc<reqwest::Client>> = None;
        static INIT: Once = Once::new();

        INIT.call_once(|| {
            let mut headers = reqwest::header::HeaderMap::new();
            let ua = format!(
                concat!(
                    env!("CARGO_PKG_NAME"),
                    "/",
                    env!("CARGO_PKG_VERSION"),
                    " (+https://t.me/{})"
                ),
                crate::BOT_NAME.get().expect("BOT_NAME not initialized")
            );
            headers.insert(
                reqwest::header::USER_AGENT,
                reqwest::header::HeaderValue::from_str(&ua).unwrap(),
            );
            let client = reqwest::Client::builder()
                .timeout(Duration::from_secs(10))
                .default_headers(headers)
                .redirect(reqwest::redirect::Policy::limited(5))
                .build()
                .unwrap();

            unsafe {
                CLIENT = Some(Arc::new(client));
            }
        });

        unsafe { CLIENT.clone() }.unwrap()
    }

    pub async fn rss(
        db: Arc<Mutex<Database>>,
        cmd: Arc<Command<Text<Https>>>,
    ) -> anyhow::Result<()> {
        let chat_id = cmd.chat.id;
        let channel = &cmd.text.value;
        let mut target_id = chat_id;

        if !channel.is_empty() {
        let user_id = cmd.from.as_ref().unwrap().id;
        let channel_id = check_channel_permission(
            &cmd.bot,
            channel,
            MsgTarget::new(chat_id, cmd.message_id),
            user_id,
        )
            .await?;
        if channel_id.is_none() {
            return Ok(());
        }
        target_id = channel_id.unwrap();
        }

        let feeds = db.lock().unwrap().subscribed_feeds(target_id.0);
        let msgs = if let Some(feeds) = feeds {
            format_and_split_msgs("订阅列表：".to_string(), &feeds, |feed| {
                format!(
                    "{}",
                    Escape(&feed.link),
                    Escape(&feed.title)
                )
            })
        } else {
            vec!["订阅列表为空".to_string()]
        };

        let mut prev_msg = cmd.message_id;
        for msg in msgs {
            let text = parameters::Text::html(&msg);
            let msg = cmd
                .bot
                .send_message(chat_id, text)
                .reply_to_message_id(prev_msg)
                .call()
                .await?;
            prev_msg = msg.id;
        }
        Ok(())
    }

    pub async fn sub(
        db: Arc<Mutex<Database>>,
        cmd: Arc<Command<Text<Https>>>,
    ) -> anyhow::Result<()> {
        let chat_id = cmd.chat.id;
        let text = &cmd.text.value;
        let args = text.split_whitespace().collect::<Vec<_>>();
        let mut target_id = chat_id;
        let feed_url;

        match &*args {
            [url] => feed_url = url,
            [channel, url] => {
                let user_id = cmd.from.as_ref().unwrap().id;
                let channel_id = check_channel_permission(
                    &cmd.bot,
                    channel,
                    MsgTarget::new(chat_id, cmd.message_id),
                    user_id,
                )
                .await?;
                if channel_id.is_none() {
                    return Ok(());
                }
                target_id = channel_id.unwrap();
                feed_url = url;
            }
            [..] => {
                return Ok(());
            }
        };
        let msg = match pull_feed(feed_url).await {
            Ok(feed) => match db.lock().unwrap().subscribe(target_id.0, feed_url, &feed) {
                Ok(()) => format!("{} 订阅成功", feed.title),
                Err(DataError::Subscribed) => "".into(),
                Err(e) => unreachable!(e),
            },
            Err(e) => format!("订阅失败 {}", Escape(&e.to_string())),
        };
        update_response(
            &cmd.bot,
            MsgTarget::new(chat_id, cmd.message_id),
            parameters::Text::html(&msg),
        )
        .await?;
        Ok(())
    }

    async fn pull_feed(url: &str) -> anyhow::Result<RSS> {
        let mut resp = client().get(url).send().await?.error_for_status()?;
        if let Some(len) = resp.content_length() {
            if len > RESP_SIZE_LIMIT as u64 {
                return Err(anyhow::format_err!("too big"));
            }
        }
        let mut buf = Vec::new(); // TODO: capacity?
        while let Some(bytes) = resp.chunk().await? {
            if buf.len() + bytes.len() > RESP_SIZE_LIMIT {
                return Err(anyhow::format_err!("too big"));
            }
            buf.extend_from_slice(&bytes);
        }

        crate::feed::parse(std::io::Cursor::new(buf))
    }

    async fn update_response(
        bot: &tbot::Bot<Https>,
        target: MsgTarget,
        message: parameters::Text<'_>,
    ) -> Result<MsgTarget, tbot::errors::MethodCall> {
        let msg = if target.first_time {
            bot.send_message(target.chat_id, message)
                .reply_to_message_id(target.message_id)
                .call()
                .await?
        } else {
            bot.edit_message_text(target.chat_id, target.message_id, message)
                .call()
                .await?
        };
        Ok(target.update(msg.id))
    }

    async fn check_channel_permission(
        bot: &tbot::Bot<Https>,
        channel: &str,
        target: MsgTarget,
        user_id: tbot::types::user::Id,
    ) -> Result<Option<tbot::types::chat::Id>, tbot::errors::MethodCall> {
        let channel_id = channel
            .parse::<i64>()
            .map(|id| parameters::ChatId::Id(id.into()))
            .unwrap_or_else(|_| {
                if channel.starts_with('@') {
                    parameters::ChatId::Username(&channel[1..])
                } else {
                    parameters::ChatId::Username(channel)
                }
            });

        let chat = bot.get_chat(channel_id).call().await?;
        if !chat.kind.is_channel() {
            update_response(bot, target, parameters::Text::plain("目标需为 Channel")).await?;
            return Ok(None);
        }
        let admins = bot.get_chat_administrators(channel_id).call().await?;
        let user_is_admin = admins
            .iter()
            .find(|member| member.user.id == user_id)
            .is_some();
        if !user_is_admin {
            update_response(
                bot,
                target,
                parameters::Text::plain("该命令只能由 Channel 管理员使用"),
            )
            .await?;
            return Ok(None);
        }
        let bot_is_admin = admins
            .iter()
            .find(|member| member.user.id == *crate::BOT_ID.get().unwrap())
            .is_some();
        if !bot_is_admin {
            update_response(
                bot,
                target,
                parameters::Text::plain("请将本 Bot 设为管理员"),
            )
            .await?;
            return Ok(None);
        }
        Ok(Some(chat.id))
    }

    pub fn format_and_split_msgs<T, F>(head: String, data: &[T], line_format_fn: F) -> Vec<String>
    where
        F: Fn(&T) -> String,
    {
        let mut msgs = vec![head];
        for item in data {
            let line = line_format_fn(item);
            if msgs.last_mut().unwrap().len() + line.len() > TELEGRAM_MAX_MSG_LEN {
                msgs.push(line);
            } else {
                let msg = msgs.last_mut().unwrap();
                msg.push('\n');
                msg.push_str(&line);
            }
        }
        msgs
    }

    pub struct Escape<'a>(pub &'a str);

    impl<'a> ::std::fmt::Display for Escape<'a> {
        fn fmt(&self, fmt: &mut ::std::fmt::Formatter) -> ::std::fmt::Result {
            // https://core.telegram.org/bots/api#html-style
            let Escape(s) = *self;
            let pile_o_bits = s;
            let mut last = 0;
            for (i, ch) in s.bytes().enumerate() {
                match ch as char {
                    '<' | '>' | '&' | '"' => {
                        fmt.write_str(&pile_o_bits[last..i])?;
                        let s = match ch as char {
                            '>' => ">",
                            '<' => "<",
                            '&' => "&",
                            '"' => """,
                            _ => unreachable!(),
                        };
                        fmt.write_str(s)?;
                        last = i + 1;
                    }
                    _ => {}
                }
            }

            if last < s.len() {
                fmt.write_str(&pile_o_bits[last..])?;
            }
            Ok(())
        }
    }
}
<|endoftext|>


<commit_before>extern crate piston_window;
extern crate find_folder;
extern crate gfx_device_gl;
extern crate gfx_graphics;
extern crate gfx;
extern crate vecmath;

use piston_window::*;

mod creature;
mod player;
mod coord;

use player::Player;
use coord::Coordinates;

//EINGABEN
const TWO_PLAYER: bool = true;
const SPRITE_P_1: &'static str = "warrior2.png";
const SPRITE_P_2: &'static str = "paladin.png";
const LEVEL_HEIGHT: u64 = 100;
const LEVEL_WIDTH: u64 = 100;

const WIDTH:  i64 = 1200;
const HEIGHT: i64 = 600;
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const RED:   [f32; 4] = [1.0, 0.0, 0.0, 1.0];
//const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
//const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];

struct Coord {
    x: f64,
    y: f64,
}

impl Coord {
    fn new(x: f64, y: f64) -> Self {
        Coord {
            x: x,
            y: y,
        }
    }
    fn origin() -> Self {
        Coord::new(0.0,0.0)
    }
    fn get_coord(&self) ->(f64, f64) {
        (self.x, self.y)
    }
}

pub struct App {
    player_one: Player,
    player_two: Option<Player>,
}

impl App {
    fn new(two_player: bool) -> Self {
        App {
            player_one: Player::new(),
            player_two: if two_player {
                Some(Player::new())
            } else {
                None
            },
        }
    }

    fn on_draw(&mut self, args: &RenderArgs, mut w: &mut PistonWindow, e: &Event) {
        let player_one = &self.player_one.creature;
        let player_two = &self.player_two;
        w.draw_2d(e, |c, gl| {
            // Clear the screen.
            clear(GREEN, gl);
            let center = c.transform.trans(0.0, 0.0);
            player_one.render(gl, center);
            if let Some(ref x) = *player_two {
                x.creature.render(gl, center);
            }
        });
    }

    fn on_update(&mut self, args: &UpdateArgs) {
        // Rotate 2 radians per second.
        self.player_one.on_update(args);
        if let Some(ref mut x) = self.player_two {
            x.on_update(args);
        }
    }

    fn on_input(&mut self, inp: Button, pressed: bool) {

        match inp {
            Button::Keyboard(Key::Up) => {
                self.player_one.up_d = pressed;
            }
            Button::Keyboard(Key::Down) => {
                self.player_one.down_d = pressed;
            }
            Button::Keyboard(Key::Left) => {
                self.player_one.left_d = pressed;
            }
            Button::Keyboard(Key::Right) => {
                self.player_one.right_d = pressed;
            }
            Button::Keyboard(Key::W) => {
                if let Some(ref mut x) = self.player_two {
                    x.up_d = pressed;
                }
            }
             Button::Keyboard(Key::S) => {
                if let Some(ref mut x) = self.player_two {
                    x.down_d = pressed;
                }
            }
             Button::Keyboard(Key::A) => {
                if let Some(ref mut x) = self.player_two {
                    x.left_d = pressed;
                }
            }
             Button::Keyboard(Key::D) => {
                if let Some(ref mut x) = self.player_two {
                    x.right_d = pressed;
                }
            }

            _ => {}
        }
    }
    fn on_load(&mut self, mut w: &mut PistonWindow) {
        let mut player_one = &mut self.player_one;
        App::load_sprite(w, &mut player_one, SPRITE_P_1);
        if let Some(ref mut x) = self.player_two {
           App::load_sprite(w, x, SPRITE_P_2);
        }

    }
    fn load_sprite(mut w: &mut PistonWindow, player: &mut Player, file: &str) {
        let assets = find_folder::Search::ParentsThenKids(3, 3).for_folder("assets").unwrap();
        let tank_sprite = assets.join(file);
        let tank_sprite2 = Texture::from_path(&mut w.factory,
                                              &tank_sprite,
                                              Flip::None,
                                              &TextureSettings::new());
        match tank_sprite2 {
            Err(_) => {
                println!("Empty");
            }
            Ok(x) => {
                player.creature.set_sprite(x);
            }
        }

    }
}

fn main() {
    // Create an Glutin window.
    let mut window: PistonWindow = WindowSettings::new("spinning-square",
                                                       [WIDTH as u32, HEIGHT as u32])
        .exit_on_esc(true)
        .build()
        .unwrap();

    // Create a new game and run it.
    let mut app = App::new(TWO_PLAYER);
    app.on_load(&mut window);

    let mut events = window.events();

    while let Some(e) = events.next(&mut window) {
        if let Some(r) = e.render_args() {
            app.on_draw(&r, &mut window, &e);
        }

        if let Some(u) = e.update_args() {
            app.on_update(&u);
        }

        if let Some(i) = e.press_args() {
            app.on_input(i, true);
        }
        if let Some(i) = e.release_args() {
            app.on_input(i, false);
        }
    }
}

//UTIL//////////////////////////////

fn border_add(add1: f64, add2: f64, width: bool) -> f64 {
    let border = (if width { WIDTH } else { HEIGHT }) as f64;
    let sum = add1 + add2;
    if sum < 0.0 {
        0.0
    } else if sum > border {
        border
    } else {
        sum
    }
}
<commit_msg>resolve errors 2<commit_after>extern crate piston_window;
extern crate find_folder;
extern crate gfx_device_gl;
extern crate gfx_graphics;
extern crate gfx;
extern crate vecmath;

use piston_window::*;

mod creature;
mod player;
mod coord;

use player::Player;
use coord::Coordinate;

//EINGABEN
const TWO_PLAYER: bool = true;
const SPRITE_P_1: &'static str = "warrior2.png";
const SPRITE_P_2: &'static str = "paladin.png";
const LEVEL_HEIGHT: u64 = 100;
const LEVEL_WIDTH: u64 = 100;

const WIDTH:  i64 = 1200;
const HEIGHT: i64 = 600;
const GREEN: [f32; 4] = [0.0, 1.0, 0.0, 1.0];
const RED:   [f32; 4] = [1.0, 0.0, 0.0, 1.0];
//const BLACK: [f32; 4] = [0.0, 0.0, 0.0, 1.0];
//const WHITE: [f32; 4] = [1.0, 1.0, 1.0, 1.0];

struct Coord {
    x: f64,
    y: f64,
}

impl Coord {
    fn new(x: f64, y: f64) -> Self {
        Coord {
            x: x,
            y: y,
        }
    }
    fn origin() -> Self {
        Coord::new(0.0,0.0)
    }
    fn get_coord(&self) ->(f64, f64) {
        (self.x, self.y)
    }
}

pub struct App {
    player_one: Player,
    player_two: Option<Player>,
}

impl App {
    fn new(two_player: bool) -> Self {
        App {
            player_one: Player::new(),
            player_two: if two_player {
                Some(Player::new())
            } else {
                None
            },
        }
    }

    fn on_draw(&mut self, args: &RenderArgs, mut w: &mut PistonWindow, e: &Event) {
        let player_one = &self.player_one.creature;
        let player_two = &self.player_two;
        w.draw_2d(e, |c, gl| {
            // Clear the screen.
            clear(GREEN, gl);
            let center = c.transform.trans(0.0, 0.0);
            player_one.render(gl, center);
            if let Some(ref x) = *player_two {
                x.creature.render(gl, center);
            }
        });
    }

    fn on_update(&mut self, args: &UpdateArgs) {
        // Rotate 2 radians per second.
        self.player_one.on_update(args);
        if let Some(ref mut x) = self.player_two {
            x.on_update(args);
        }
    }

    fn on_input(&mut self, inp: Button, pressed: bool) {

        match inp {
            Button::Keyboard(Key::Up) => {
                self.player_one.up_d = pressed;
            }
            Button::Keyboard(Key::Down) => {
                self.player_one.down_d = pressed;
            }
            Button::Keyboard(Key::Left) => {
                self.player_one.left_d = pressed;
            }
            Button::Keyboard(Key::Right) => {
                self.player_one.right_d = pressed;
            }
            Button::Keyboard(Key::W) => {
                if let Some(ref mut x) = self.player_two {
                    x.up_d = pressed;
                }
            }
             Button::Keyboard(Key::S) => {
                if let Some(ref mut x) = self.player_two {
                    x.down_d = pressed;
                }
            }
             Button::Keyboard(Key::A) => {
                if let Some(ref mut x) = self.player_two {
                    x.left_d = pressed;
                }
            }
             Button::Keyboard(Key::D) => {
                if let Some(ref mut x) = self.player_two {
                    x.right_d = pressed;
                }
            }

            _ => {}
        }
    }
    fn on_load(&mut self, mut w: &mut PistonWindow) {
        let mut player_one = &mut self.player_one;
        App::load_sprite(w, &mut player_one, SPRITE_P_1);
        if let Some(ref mut x) = self.player_two {
           App::load_sprite(w, x, SPRITE_P_2);
        }

    }
    fn load_sprite(mut w: &mut PistonWindow, player: &mut Player, file: &str) {
        let assets = find_folder::Search::ParentsThenKids(3, 3).for_folder("assets").unwrap();
        let tank_sprite = assets.join(file);
        let tank_sprite2 = Texture::from_path(&mut w.factory,
                                              &tank_sprite,
                                              Flip::None,
                                              &TextureSettings::new());
        match tank_sprite2 {
            Err(_) => {
                println!("Empty");
            }
            Ok(x) => {
                player.creature.set_sprite(x);
            }
        }

    }
}

fn main() {
    // Create an Glutin window.
    let mut window: PistonWindow = WindowSettings::new("spinning-square",
                                                       [WIDTH as u32, HEIGHT as u32])
        .exit_on_esc(true)
        .build()
        .unwrap();

    // Create a new game and run it.
    let mut app = App::new(TWO_PLAYER);
    app.on_load(&mut window);

    let mut events = window.events();

    while let Some(e) = events.next(&mut window) {
        if let Some(r) = e.render_args() {
            app.on_draw(&r, &mut window, &e);
        }

        if let Some(u) = e.update_args() {
            app.on_update(&u);
        }

        if let Some(i) = e.press_args() {
            app.on_input(i, true);
        }
        if let Some(i) = e.release_args() {
            app.on_input(i, false);
        }
    }
}

//UTIL//////////////////////////////

fn border_add(add1: f64, add2: f64, width: bool) -> f64 {
    let border = (if width { WIDTH } else { HEIGHT }) as f64;
    let sum = add1 + add2;
    if sum < 0.0 {
        0.0
    } else if sum > border {
        border
    } else {
        sum
    }
}
<|endoftext|>


<commit_before>extern crate iron;
extern crate hoedown;
extern crate hyper;
extern crate webbrowser;
extern crate router;
extern crate staticfile;
extern crate clap;
extern crate linked_hash_map;

#[macro_use]
extern crate lazy_static;

use iron::prelude::*;

use std::path::Path;
use std::fs;

use router::Router;
use staticfile::Static;

use clap::{Arg, App};

#[macro_use]
mod macros;

mod git;
mod api;
mod views;
mod yaml_utils;
mod text_files;
mod configuration;

fn main() {
    let matches = arg_matches();

    if check_data_directory() {
        let repo = git::open_or_init(".cardboard");
        let state = repo.head().ok()
            .and_then( |state| {
                let msg = state.target()
                    .and_then(|oid| repo.find_commit(oid).ok())
                    .and_then(|mut commit| commit.summary().map(|s| s.to_string()))
                    .unwrap_or(String::from("no summary"));

                state.shorthand().map(|s| format!("{} ({})", s.to_string(), msg))
            })
            .unwrap_or(String::from("<new>"));

        if state == "<new>" {
            let sha = git::initial_commit(".", "initial commit", &repo);

            println!("Data at {:?}", sha);
        } else {
            println!("Data at {}", state);
        }

        println!("Listening on port 9000 ...");

        if no_browser(& matches) {
            println!("Open http://localhost:9000 to view your project")
        }

        Iron::new(make_router()).http("localhost:9000").unwrap();
    } else {
        println!("Failed to create data directory");
    }
}

fn arg_matches<'a>() -> clap::ArgMatches<'a> {
    App::new("cardboard")
        .version("v0.1")
        .author("Markus Kahl <machisuji@gmail.com>")
        .arg(Arg::with_name("quiet")
            .short("q")
            .long("quiet")
            .help("Starts cardboard without opening it in the browser.")
        )
        .get_matches()
}

fn make_router() -> Router {
    let mut router = Router::new();

    router.get("/", views::index, "index");
    router.get("*", file_handler(), "files");

    router.post("/update_card", api::update_card, "update_card");

    router
}

fn file_handler() -> Static {
    Static::new(Path::new("public/"))
}

fn check_data_directory() -> bool {
    let root = ".cardboard";
    let cards = root.to_string() + "/cards";
    let default_data = fs::create_dir(root).is_ok();

    // populate newly created .cardboard directory with example data
    if default_data {
        println!("No data found. Initializing with example data.");
        copy_directory("public/example", ".cardboard");
    }

    !default_data || Path::new(& cards).exists()
}

/**
 * Copies the given directory to another location as far as possible.
 * Meaning that it will continue even if single files or subdirectories
 * cannot be copied.
 */
fn copy_directory(from_path: &str, to_path: &str) {
    if let Ok(entries) = fs::read_dir(from_path) {
        for entry in entries.into_iter().filter_map(|e| e.ok()) {
            let path = entry.path();

            if let Some(Some(file_name)) = path.file_name().map(|name| name.to_str()) {
                if let Some(path_name) = path.to_str() {
                    let new_path = format!("{}/{}", to_path, file_name);

                    if path.is_dir() {
                        if fs::create_dir(&new_path).is_ok() || Path::new(&new_path).exists() {
                            copy_directory(path_name, &new_path);
                        } else {
                            println!("Failed to copy directory {} to {}", path_name, new_path);
                        }
                    } else {
                        if fs::copy(&path, Path::new(&new_path)).is_err() {
                            println!("Failed to copy {} to {}", path_name, new_path);
                        }
                    }
                }
            }
        }
    }
}

fn no_browser(matches: &clap::ArgMatches) -> bool {
    matches.occurrences_of("quiet") == 1 || !webbrowser::open("http://localhost:9000/").is_ok()
}
<commit_msg>separate messages for new/existing data<commit_after>extern crate iron;
extern crate hoedown;
extern crate hyper;
extern crate webbrowser;
extern crate router;
extern crate staticfile;
extern crate clap;
extern crate linked_hash_map;

#[macro_use]
extern crate lazy_static;

use iron::prelude::*;

use std::path::Path;
use std::fs;

use router::Router;
use staticfile::Static;

use clap::{Arg, App};

#[macro_use]
mod macros;

mod git;
mod api;
mod views;
mod yaml_utils;
mod text_files;
mod configuration;

fn main() {
    let matches = arg_matches();

    if check_data_directory() {
        let repo = git::open_or_init(".cardboard");
        let state = repo.head().ok()
            .and_then( |state| {
                let msg = state.target()
                    .and_then(|oid| repo.find_commit(oid).ok())
                    .and_then(|mut commit| commit.summary().map(|s| s.to_string()))
                    .unwrap_or(String::from("no summary"));

                state.shorthand().map(|s| format!("{} ({})", s.to_string(), msg))
            })
            .unwrap_or(String::from("<new>"));

        if state == "<new>" {
            let sha = git::initial_commit(".", "initial commit", &repo);

            println!("Initialized data at {:?}", sha);
        } else {
            println!("Existing data at {}", state);
        }

        println!("Listening on port 9000 ...");

        if no_browser(& matches) {
            println!("Open http://localhost:9000 to view your project")
        }

        Iron::new(make_router()).http("localhost:9000").unwrap();
    } else {
        println!("Failed to create data directory");
    }
}

fn arg_matches<'a>() -> clap::ArgMatches<'a> {
    App::new("cardboard")
        .version("v0.1")
        .author("Markus Kahl <machisuji@gmail.com>")
        .arg(Arg::with_name("quiet")
            .short("q")
            .long("quiet")
            .help("Starts cardboard without opening it in the browser.")
        )
        .get_matches()
}

fn make_router() -> Router {
    let mut router = Router::new();

    router.get("/", views::index, "index");
    router.get("*", file_handler(), "files");

    router.post("/update_card", api::update_card, "update_card");

    router
}

fn file_handler() -> Static {
    Static::new(Path::new("public/"))
}

fn check_data_directory() -> bool {
    let root = ".cardboard";
    let cards = root.to_string() + "/cards";
    let default_data = fs::create_dir(root).is_ok();

    // populate newly created .cardboard directory with example data
    if default_data {
        println!("No data found. Initializing with example data.");
        copy_directory("public/example", ".cardboard");
    }

    !default_data || Path::new(& cards).exists()
}

/**
 * Copies the given directory to another location as far as possible.
 * Meaning that it will continue even if single files or subdirectories
 * cannot be copied.
 */
fn copy_directory(from_path: &str, to_path: &str) {
    if let Ok(entries) = fs::read_dir(from_path) {
        for entry in entries.into_iter().filter_map(|e| e.ok()) {
            let path = entry.path();

            if let Some(Some(file_name)) = path.file_name().map(|name| name.to_str()) {
                if let Some(path_name) = path.to_str() {
                    let new_path = format!("{}/{}", to_path, file_name);

                    if path.is_dir() {
                        if fs::create_dir(&new_path).is_ok() || Path::new(&new_path).exists() {
                            copy_directory(path_name, &new_path);
                        } else {
                            println!("Failed to copy directory {} to {}", path_name, new_path);
                        }
                    } else {
                        if fs::copy(&path, Path::new(&new_path)).is_err() {
                            println!("Failed to copy {} to {}", path_name, new_path);
                        }
                    }
                }
            }
        }
    }
}

fn no_browser(matches: &clap::ArgMatches) -> bool {
    matches.occurrences_of("quiet") == 1 || !webbrowser::open("http://localhost:9000/").is_ok()
}
<|endoftext|>


<commit_before>extern crate piston_window;
#[macro_use]
extern crate clap;
extern crate rand;
extern crate find_folder;

mod game;
mod field;
mod ui;
mod common;

use piston_window::*;
use clap::{Arg, App};

fn main() {
    let matches = App::new("Minesweeper")
        .author("Alexander Kuvaev <alexander@kuvaev.me>")
        .version(&*format!("v{}", crate_version!()))
        .about("Simple minesweeper in Rust ")
        .arg(Arg::from_usage("-s, --size [size] 'window size \"width\"x\"height\"'")
            .validator(|val| {
                let mut s = val.split("x");
                match s.next() {
                    Some(w) => {
                        match w.parse::<u32>() {
                            Ok(r) => {
                                if (r < 100) || (r > 4096) {
                                    return Err(format!("window width must be between 100 and 4096"))
                                }
                            },
                            Err(e) => return Err(format!("window width arg error: {}", e))
                        }
                    }
                    None => return Err(format!("window size expected \"width\"x\"height\", got {}", val))
                }
                match s.next() {
                    Some(h) => {
                        match h.parse::<u32>() {
                            Ok(r) => {
                                if (r < 100) || (r > 3072) {
                                    return Err(format!("window height must be between 100 and 3072"))
                                }
                            },
                            Err(e) => return Err(format!("window height arg error: {}", e))
                        }
                    }
                    None => return Err(format!("window size expected \"width\"x\"height\", got {}", val))
                }
                match s.next() {
                    Some(_r) => Err(format!("window size arg expected \"width\"x\"height\", got {}", val)),
                    None => Ok(())
                }
            }))
        .arg(Arg::from_usage("-f, --field [field] 'field size \"width\"x\"height\"'")
            .validator(|val| {
                let mut s = val.split("x");
                match s.next() {
                    Some(w) => {
                        match w.parse::<u32>() {
                            Ok(r) => {
                                if (r < 5) || (r > 100) {
                                    return Err(format!("field width must be between 5 and 100"))
                                }
                            },
                            Err(e) => return Err(format!("field width arg error: {}", e))
                        }
                    }
                    None => return Err(format!("field size expected \"width\"x\"height\", got {}", val))
                }
                match s.next() {
                    Some(h) => {
                        match h.parse::<u32>() {
                            Ok(r) => {
                                if (r < 5) || (r > 100) {
                                    return Err(format!("field height must be between 5 and 100"))
                                }
                            },
                            Err(e) => return Err(format!("field height arg error: {}", e))
                        }
                    }
                    None => return Err(format!("field size expected \"width\"x\"height\", got {}", val))
                }
                match s.next() {
                    Some(_r) => Err(format!("field size arg expected \"width\"x\"height\", got {}", val)),
                    None => Ok(())
                }
            }))
        .arg(Arg::from_usage("-m, --mines [mines] 'max mines'"))
        .arg(Arg::from_usage("--oldOGL 'set OpenGL version to 2.1'"))
        .get_matches();

    let mut width = 1024;
    let mut height = 768;
    let mut f_width = 20;
    let mut f_height = 20;
    let mut mines = 50;
    let mut opengl = OpenGL::V3_2;
    if let Some(size) = matches.value_of("size") {
        let mut s = size.split("x");
        width = s.next().unwrap().parse().unwrap_or(width);
        height = s.next().unwrap().parse().unwrap_or(height);
    }
    if let Some(field) = matches.value_of("field") {
        let mut s = field.split("x");
        f_width = s.next().unwrap().parse().unwrap_or(f_width);
        f_height = s.next().unwrap().parse().unwrap_or(f_height);
    }
    if let Some(m) = matches.value_of("mines") {
        mines = m.parse().unwrap_or(mines);
    }
    if matches.is_present("oldOGL") {
        opengl = OpenGL::V2_1;
    }

    let window: PistonWindow =
        WindowSettings::new("Minesweeper", [width, height])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let assets = find_folder::Search::ParentsThenKids(3, 3)
        .for_folder("assets").unwrap();
    let ref font = assets.join("FiraSans-Regular.ttf");
    let factory = window.factory.borrow().clone();
    let glyphs = Glyphs::new(font, factory).unwrap();

    let mut game = game::Game::new(glyphs, f_width, f_height, mines);

    for e in window {
        game.render(&e);

        if let Some(mouse_rel) = e.mouse_cursor_args() {
            game.mouse_move(mouse_rel);
        }

        if let Some(button) = e.press_args() {
            game.proc_key(button, &e);
        }
    }
}
<commit_msg>feat(Game): Add ability to set max fps<commit_after>extern crate piston_window;
#[macro_use]
extern crate clap;
extern crate rand;
extern crate find_folder;

mod game;
mod field;
mod ui;
mod common;

use piston_window::*;
use clap::{Arg, App};

fn main() {
    let matches = App::new("Minesweeper")
        .author("Alexander Kuvaev <alexander@kuvaev.me>")
        .version(&*format!("v{}", crate_version!()))
        .about("Simple minesweeper in Rust ")
        .arg(Arg::from_usage("-s, --size [size] 'window size \"width\"x\"height\"'")
            .validator(|val| {
                let mut s = val.split("x");
                match s.next() {
                    Some(w) => {
                        match w.parse::<u32>() {
                            Ok(r) => {
                                if (r < 100) || (r > 4096) {
                                    return Err(format!("window width must be between 100 and 4096"))
                                }
                            },
                            Err(e) => return Err(format!("window width arg error: {}", e))
                        }
                    }
                    None => return Err(format!("window size expected \"width\"x\"height\", got {}", val))
                }
                match s.next() {
                    Some(h) => {
                        match h.parse::<u32>() {
                            Ok(r) => {
                                if (r < 100) || (r > 3072) {
                                    return Err(format!("window height must be between 100 and 3072"))
                                }
                            },
                            Err(e) => return Err(format!("window height arg error: {}", e))
                        }
                    }
                    None => return Err(format!("window size expected \"width\"x\"height\", got {}", val))
                }
                match s.next() {
                    Some(_r) => Err(format!("window size arg expected \"width\"x\"height\", got {}", val)),
                    None => Ok(())
                }
            }))
        .arg(Arg::from_usage("-f, --field [field] 'field size \"width\"x\"height\"'")
            .validator(|val| {
                let mut s = val.split("x");
                match s.next() {
                    Some(w) => {
                        match w.parse::<u32>() {
                            Ok(r) => {
                                if (r < 5) || (r > 100) {
                                    return Err(format!("field width must be between 5 and 100"))
                                }
                            },
                            Err(e) => return Err(format!("field width arg error: {}", e))
                        }
                    }
                    None => return Err(format!("field size expected \"width\"x\"height\", got {}", val))
                }
                match s.next() {
                    Some(h) => {
                        match h.parse::<u32>() {
                            Ok(r) => {
                                if (r < 5) || (r > 100) {
                                    return Err(format!("field height must be between 5 and 100"))
                                }
                            },
                            Err(e) => return Err(format!("field height arg error: {}", e))
                        }
                    }
                    None => return Err(format!("field size expected \"width\"x\"height\", got {}", val))
                }
                match s.next() {
                    Some(_r) => Err(format!("field size arg expected \"width\"x\"height\", got {}", val)),
                    None => Ok(())
                }
            }))
        .arg(Arg::from_usage("-m, --mines [mines] 'max mines'"))
        .arg(Arg::from_usage("--oldOGL 'set OpenGL version to 2.1'"))
        .arg(Arg::from_usage("--maxFPS [max_fps] 'set max fps'"))
        .get_matches();

    let mut width = 1024;
    let mut height = 768;
    let mut f_width = 20;
    let mut f_height = 20;
    let mut mines = 50;
    let mut max_fps = 60;
    let mut opengl = OpenGL::V3_2;
    if let Some(size) = matches.value_of("size") {
        let mut s = size.split("x");
        width = s.next().unwrap().parse().unwrap_or(width);
        height = s.next().unwrap().parse().unwrap_or(height);
    }
    if let Some(field) = matches.value_of("field") {
        let mut s = field.split("x");
        f_width = s.next().unwrap().parse().unwrap_or(f_width);
        f_height = s.next().unwrap().parse().unwrap_or(f_height);
    }
    if let Some(m) = matches.value_of("mines") {
        mines = m.parse().unwrap_or(mines);
    }
    if let Some(m) = matches.value_of("max_fps") {
        max_fps = m.parse().unwrap_or(max_fps);
    }
    if matches.is_present("oldOGL") {
        opengl = OpenGL::V2_1;
    }

    let mut window: PistonWindow =
        WindowSettings::new("Minesweeper", [width, height])
        .opengl(opengl)
        .exit_on_esc(true)
        .build()
        .unwrap();

    let assets = find_folder::Search::ParentsThenKids(3, 3)
        .for_folder("assets").unwrap();
    let ref font = assets.join("FiraSans-Regular.ttf");
    let factory = window.factory.borrow().clone();
    let glyphs = Glyphs::new(font, factory).unwrap();

    let mut game = game::Game::new(glyphs, f_width, f_height, mines);
    window.set_max_fps(max_fps);
    for e in window {
        game.render(&e);

        if let Some(mouse_rel) = e.mouse_cursor_args() {
            game.mouse_move(mouse_rel);
        }

        if let Some(button) = e.press_args() {
            game.proc_key(button, &e);
        }
    }
}
<|endoftext|>


<commit_before>extern crate turtle;

use std::fmt;
use std::fs::File;
use turtle::{Canvas, Turtle};

#[derive(Debug, Eq, PartialEq, Clone)]
struct Character(char);

#[derive(Eq, PartialEq, Clone)]
struct Symbol {
    character: Character,
}

impl fmt::Debug for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.character.0)
    }
}

#[derive(Clone)]
struct SymbolString(Vec<Symbol>);

impl fmt::Debug for SymbolString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let v = &self.0;
        try!(write!(f, "\""));
        if !v.is_empty() {
            try!(write!(f, "{:?}", v[0]));
            for e in &v[1..] {
                try!(write!(f, " {:?}", e));
            }
        }
        write!(f, "\"")
    }
}

impl SymbolString {
    fn from_str(s: &str) -> SymbolString {
        SymbolString(s.chars().filter(|&c| !c.is_whitespace()).map(|c| Symbol{character: Character(c)}).collect())
    }
}

#[derive(Debug)]
struct Rule {
    lhs: Symbol,
    rhs: SymbolString,
}

impl Rule {
    fn produce(&self, sym: &Symbol) -> Option<SymbolString> {
        if self.lhs.eq(sym) {
            Some(self.rhs.clone())
        } else {
            None
        }
    }
}

#[derive(Debug)]
struct System {
    rules: Vec<Rule>,
}

impl System {
    fn develop(&self, axiom: SymbolString, iterations: usize) -> (SymbolString, usize) {
        let mut current = axiom;

        for iter in 0..iterations {
            let (next, progress) = self.develop1(&current);
            if !progress {
                return (current, iter);
            }
            current = next;
        }
        return (current, iterations);
    }

    fn develop1(&self, axiom: &SymbolString) -> (SymbolString, bool) {
        let mut expanded = Vec::new();
        let mut any_rule_applied = false;

        for symbol in axiom.0.iter() {
            let mut found = false;
            for rule in self.rules.iter() {
                if let Some(rhs) = rule.produce(&symbol) {
                    expanded.extend(rhs.0);
                    any_rule_applied = true;
                    found = true;
                    break;
                }
            }
            if !found {
                expanded.push(symbol.clone());
            }
        }

        (SymbolString(expanded), any_rule_applied)
    }
}
fn sym(c: char) -> Symbol {
    Symbol { character: Character(c) }
}

fn draw(symstr: &SymbolString, init_direction: f32, angle: f32, distance: f32, filename: &str) {
    let mut t = Canvas::new();
    t.pendown();
    t.right(init_direction);
    for sym in symstr.0.iter() {
        match sym.character.0 {
            'F' => t.forward(distance),
            '+' => t.right(angle),
            '-' => t.left(angle),
            '[' => t.push(),
            ']' => t.pop(),
            _   => {}
        }
    }
    t.save_svg(&mut File::create(filename).unwrap()).unwrap();
}

fn koch_curve(maxiter: usize) {
    let axiom = SymbolString::from_str("F++F++F");

    let system = System { rules: vec![
        Rule {
            lhs: sym('F'),
            rhs: SymbolString::from_str("F-F++F-F")
        }
        ] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, 0.0, 60.0, 10.0, &format!("koch_{:02}.svg", iters));
}

fn dragon_curve(maxiter: usize) {
    let axiom = SymbolString::from_str("FX");

    let system = System { rules: vec![
        Rule {
            lhs: sym('X'),
            rhs: SymbolString::from_str("X+YF+")
        },
        Rule {
            lhs: sym('Y'),
            rhs: SymbolString::from_str("-FX-Y")
        }
        ] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, -90.0, 90.0, 10.0, &format!("dragon_{:02}.svg", iters));
}

fn sierpinski_triangle(maxiter: usize) {
    let axiom = SymbolString::from_str("A");

    let system = System { rules: vec![
        Rule {
            lhs: sym('A'),
            rhs: SymbolString::from_str("+B-A-B+")
        },
        Rule {
            lhs: sym('B'),
            rhs: SymbolString::from_str("-A+B+A-")
        }
        ] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    // replace A and B with F
    let system = System { rules: vec![
        Rule {
            lhs: sym('A'),
            rhs: SymbolString::from_str("F")
        },
        Rule {
            lhs: sym('B'),
            rhs: SymbolString::from_str("F")
        }
        ] };
    let (after, _iters) = system.develop1(&after);

    draw(&after, 0.0, 60.0, 10.0, &format!("sierpinski_{:02}.svg", iters));
}

fn fractal_plant(maxiter: usize) {
    let axiom = SymbolString::from_str("X");

    let system = System { rules: vec![
        Rule {
            lhs: sym('X'),
            rhs: SymbolString::from_str("F-[[X]+X]+F[+FX]-X")
        },
        Rule {
            lhs: sym('F'),
            rhs: SymbolString::from_str("FF")
        }
        ] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, -90.0, 25.0, 10.0, &format!("plant_{:02}.svg", iters));
}

fn main() {
    for i in 0..7 {
        koch_curve(i);
    }
    for i in 0..16 {
        dragon_curve(i);
    }
    for i in 0..10 {
        sierpinski_triangle(i);
    }
    for i in 4..8 {
        fractal_plant(i);
    }
}
<commit_msg>Change angles<commit_after>extern crate turtle;

use std::fmt;
use std::fs::File;
use turtle::{Canvas, Turtle};

#[derive(Debug, Eq, PartialEq, Clone)]
struct Character(char);

#[derive(Eq, PartialEq, Clone)]
struct Symbol {
    character: Character,
}

impl fmt::Debug for Symbol {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "{}", self.character.0)
    }
}

#[derive(Clone)]
struct SymbolString(Vec<Symbol>);

impl fmt::Debug for SymbolString {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let v = &self.0;
        try!(write!(f, "\""));
        if !v.is_empty() {
            try!(write!(f, "{:?}", v[0]));
            for e in &v[1..] {
                try!(write!(f, " {:?}", e));
            }
        }
        write!(f, "\"")
    }
}

impl SymbolString {
    fn from_str(s: &str) -> SymbolString {
        SymbolString(s.chars().filter(|&c| !c.is_whitespace()).map(|c| Symbol{character: Character(c)}).collect())
    }
}

#[derive(Debug)]
struct Rule {
    lhs: Symbol,
    rhs: SymbolString,
}

impl Rule {
    fn produce(&self, sym: &Symbol) -> Option<SymbolString> {
        if self.lhs.eq(sym) {
            Some(self.rhs.clone())
        } else {
            None
        }
    }
}

#[derive(Debug)]
struct System {
    rules: Vec<Rule>,
}

impl System {
    fn develop(&self, axiom: SymbolString, iterations: usize) -> (SymbolString, usize) {
        let mut current = axiom;

        for iter in 0..iterations {
            let (next, progress) = self.develop1(&current);
            if !progress {
                return (current, iter);
            }
            current = next;
        }
        return (current, iterations);
    }

    fn develop1(&self, axiom: &SymbolString) -> (SymbolString, bool) {
        let mut expanded = Vec::new();
        let mut any_rule_applied = false;

        for symbol in axiom.0.iter() {
            let mut found = false;
            for rule in self.rules.iter() {
                if let Some(rhs) = rule.produce(&symbol) {
                    expanded.extend(rhs.0);
                    any_rule_applied = true;
                    found = true;
                    break;
                }
            }
            if !found {
                expanded.push(symbol.clone());
            }
        }

        (SymbolString(expanded), any_rule_applied)
    }
}
fn sym(c: char) -> Symbol {
    Symbol { character: Character(c) }
}

fn draw(symstr: &SymbolString, init_direction: f32, angle: f32, distance: f32, filename: &str) {
    let mut t = Canvas::new();
    t.pendown();
    t.right(init_direction);
    for sym in symstr.0.iter() {
        match sym.character.0 {
            'F' => t.forward(distance),
            '+' => t.right(angle),
            '-' => t.left(angle),
            '[' => t.push(),
            ']' => t.pop(),
            _   => {}
        }
    }
    t.save_svg(&mut File::create(filename).unwrap()).unwrap();
}

fn koch_curve(maxiter: usize) {
    let axiom = SymbolString::from_str("F++F++F");

    let system = System { rules: vec![
        Rule {
            lhs: sym('F'),
            rhs: SymbolString::from_str("F-F++F-F")
        }
        ] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, -90.0, 60.0, 10.0, &format!("koch_{:02}.svg", iters));
}

fn dragon_curve(maxiter: usize) {
    let axiom = SymbolString::from_str("FX");

    let system = System { rules: vec![
        Rule {
            lhs: sym('X'),
            rhs: SymbolString::from_str("X+YF+")
        },
        Rule {
            lhs: sym('Y'),
            rhs: SymbolString::from_str("-FX-Y")
        }
        ] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, 0.0, 90.0, 10.0, &format!("dragon_{:02}.svg", iters));
}

fn sierpinski_triangle(maxiter: usize) {
    let axiom = SymbolString::from_str("A");

    let system = System { rules: vec![
        Rule {
            lhs: sym('A'),
            rhs: SymbolString::from_str("+B-A-B+")
        },
        Rule {
            lhs: sym('B'),
            rhs: SymbolString::from_str("-A+B+A-")
        }
        ] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    // replace A and B with F
    let system = System { rules: vec![
        Rule {
            lhs: sym('A'),
            rhs: SymbolString::from_str("F")
        },
        Rule {
            lhs: sym('B'),
            rhs: SymbolString::from_str("F")
        }
        ] };
    let (after, _iters) = system.develop1(&after);

    draw(&after, -90.0, 60.0, 10.0, &format!("sierpinski_{:02}.svg", iters));
}

fn fractal_plant(maxiter: usize) {
    let axiom = SymbolString::from_str("X");

    let system = System { rules: vec![
        Rule {
            lhs: sym('X'),
            rhs: SymbolString::from_str("F-[[X]+X]+F[+FX]-X")
        },
        Rule {
            lhs: sym('F'),
            rhs: SymbolString::from_str("FF")
        }
        ] };
    println!("{:?}", system);

    let (after, iters) = system.develop(axiom, maxiter);

    draw(&after, 0.0, 25.0, 10.0, &format!("plant_{:02}.svg", iters));
}

fn main() {
    for i in 0..7 {
        koch_curve(i);
    }
    for i in 0..16 {
        dragon_curve(i);
    }
    for i in 0..10 {
        sierpinski_triangle(i);
    }
    for i in 4..8 {
        fractal_plant(i);
    }
}
<|endoftext|>


<commit_before>use clap::{crate_version, App, Arg};
use csv::ReaderBuilder;
use std::io;
use std::process;
use table::{ascii_writer, markdown_writer};

const DESCRIPTION: &str = "A command to print ASCII table from stdin";
const USAGE_TEMPLATE: &str = r#"
Usage:
    {usage}

Flags:
{flags}

Options:
{options}
"#;

fn main() {
    let matches = App::new("table")
        .version(crate_version!())
        .author("Naoto Kaneko <naoty.k@gmail.com>")
        .about(DESCRIPTION)
        .template(USAGE_TEMPLATE.trim())
        .version_short("v")
        .arg(
            Arg::with_name("format")
                .short("f")
                .long("format")
                .takes_value(true)
                .default_value("")
                .value_name("FORMAT")
                .help("Config input/output data format"),
        )
        .arg(
            Arg::with_name("header")
                .short("H")
                .long("header")
                .help("Prints table with headers"),
        )
        .get_matches();

    if let Err(error) = start(
        matches.value_of("format").unwrap(),
        matches.is_present("header"),
    ) {
        eprintln!("{}", error);
        process::exit(1);
    }
}

fn start(format: &str, has_headers: bool) -> io::Result<()> {
    let mut reader = ReaderBuilder::new()
        .delimiter(b'\t')
        .has_headers(false)
        .from_reader(io::stdin());

    let tokens = format.split(":").take(2);
    let mut ascii_writer = ascii_writer::new(io::stdout(), has_headers);
    let mut markdown_writer = markdown_writer::new(io::stdout());
    let writer: &mut dyn io::Write = match tokens.last() {
        Some("markdown") => &mut markdown_writer,
        _ => &mut ascii_writer,
    };

    for result in reader.records() {
        match result {
            Ok(record) => {
                for field in record.iter() {
                    writer.write(field.as_bytes())?;
                }
                writer.write(b"\n")?;
            }
            Err(error) => {
                eprintln!("failed to read record: {}", error);
                process::exit(1);
            }
        }
    }

    writer.flush()
}
<commit_msg>Rewrite main.rs to use Read and Write trait<commit_after>use clap::{crate_version, App, Arg};
use std::io;
use std::process;
use table::{reader, writer};

const DESCRIPTION: &str = "A command to print ASCII table from stdin";
const USAGE_TEMPLATE: &str = r#"
Usage:
    {usage}

Flags:
{flags}

Options:
{options}
"#;

fn main() {
    let matches = App::new("table")
        .version(crate_version!())
        .author("Naoto Kaneko <naoty.k@gmail.com>")
        .about(DESCRIPTION)
        .template(USAGE_TEMPLATE.trim())
        .version_short("v")
        .arg(
            Arg::with_name("format")
                .short("f")
                .long("format")
                .takes_value(true)
                .default_value("")
                .value_name("FORMAT")
                .help("Config input/output data format"),
        )
        .arg(
            Arg::with_name("header")
                .short("H")
                .long("header")
                .help("Prints table with headers"),
        )
        .get_matches();

    let reader: &mut dyn table::Read =
        &mut reader::CsvReader::new(io::stdin(), b',', matches.is_present("header"));
    let writer: &mut dyn table::Write = &mut writer::AsciiWriter::new(io::stdout());
    let result = reader
        .read()
        .and_then(|table| writer.write(table))
        .and_then(|_| writer.flush());

    match result {
        Ok(_) => {
            process::exit(0);
        }
        Err(error) => {
            eprintln!("{:?}", error);
            process::exit(1);
        }
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate quick_error;
extern crate toml;
extern crate clap;

use std::process::exit;

use clap::{App, ArgMatches, SubCommand};

mod config;
mod error;
mod cmd;
mod git;
mod cargo;

fn execute(args: &ArgMatches) -> Result<i32, error::FatalError> {
    let cargo_file = try!(config::parse_cargo_config());

    // Verify the TOML configuration if present.
    if let Some(invalid_keys) = config::verify_release_config(&cargo_file) {
        for i in invalid_keys {
            println!("Unknown config key \"{}\" found for [package.metadata.gh-pages]",
                     i);
        }
        return Ok(109);
    }

    // Find parameters or use defaults.
    let dry_run = args.occurrences_of("dry-run") > 0;
    let sign = args.occurrences_of("sign") > 0 ||
               config::get_release_config(&cargo_file, config::SIGN_COMMIT)
        .and_then(|f| f.as_bool())
        .unwrap_or(false);
    let git_remote = args.value_of("push-remote")
        .or_else(|| {
            config::get_release_config(&cargo_file, config::PUSH_REMOTE).and_then(|f| f.as_str())
        })
        .unwrap_or("origin");
    let doc_branch = args.value_of("doc-branch")
        .or_else(|| {
            config::get_release_config(&cargo_file, config::DOC_BRANCH).and_then(|f| f.as_str())
        })
        .unwrap_or("gh-pages");
    let doc_commit_msg = config::get_release_config(&cargo_file, config::DOC_COMMIT_MESSAGE)
        .and_then(|f| f.as_str())
        .unwrap_or("(cargo-gh-pages) Generate docs.");

    // Check if working directory is clean.
    if !try!(git::status()) {
        println!("Uncommitted changes detected, please commit before release");
        if !dry_run {
            return Ok(101);
        }
    }

    // Generate and upload documentation.
    println!("Building and exporting docs.");
    try!(cargo::doc(dry_run));

    let doc_path = "target/doc/";

    try!(git::init(doc_path, dry_run));
    try!(git::add_all(doc_path, dry_run));
    try!(git::commit_all(doc_path, doc_commit_msg, sign, dry_run));
    let remote = try!(git::remote_get_url(git_remote));

    let mut refspec = String::from("master:");
    refspec.push_str(doc_branch);

    try!(git::force_push(doc_path, remote.trim(), &refspec, dry_run));

    Ok(0)
}

static USAGE: &'static str = "[sign]... --sign 'Sign git commit'
                             [dry-run]... --dry-run 'Do not actually change anything.'
                             --push-remote=[push-remote] 'Git remote to push'
                             --doc-branch=[doc-branch] 'Git branch to push documentation on' ";

fn main() {
    let matches = App::new("cargo")
        .subcommand(SubCommand::with_name("gh-pages")
            .version(env!("CARGO_PKG_VERSION"))
            .author("Ning Sun <sunng@about.me>")
            .author("Woof Woof, Inc.")
            .about("Cargo subcommand for generating and publishing RustDoc to GitHub Pages.")
            .args_from_usage(USAGE))
        .get_matches();

    if let Some(release_matches) = matches.subcommand_matches("gh-pages") {
        match execute(release_matches) {
            Ok(code) => exit(code),
            Err(e) => {
                println!("Fatal: {}", e);
                exit(128);
            }
        }
    }
}
<commit_msg>Tweak: Use longer variable name for clarity. (#13)<commit_after>#[macro_use]
extern crate quick_error;
extern crate toml;
extern crate clap;

use std::process::exit;

use clap::{App, ArgMatches, SubCommand};

mod config;
mod error;
mod cmd;
mod git;
mod cargo;

fn execute(args: &ArgMatches) -> Result<i32, error::FatalError> {
    let cargo_file = try!(config::parse_cargo_config());

    // Verify the TOML configuration if present.
    if let Some(invalid_keys) = config::verify_release_config(&cargo_file) {
        for invalid_key in invalid_keys {
            println!("Unknown config key \"{}\" found for [package.metadata.gh-pages]",
                     invalid_key);
        }
        return Ok(109);
    }

    // Find parameters or use defaults.
    let dry_run = args.occurrences_of("dry-run") > 0;
    let sign = args.occurrences_of("sign") > 0 ||
               config::get_release_config(&cargo_file, config::SIGN_COMMIT)
        .and_then(|f| f.as_bool())
        .unwrap_or(false);
    let git_remote = args.value_of("push-remote")
        .or_else(|| {
            config::get_release_config(&cargo_file, config::PUSH_REMOTE).and_then(|f| f.as_str())
        })
        .unwrap_or("origin");
    let doc_branch = args.value_of("doc-branch")
        .or_else(|| {
            config::get_release_config(&cargo_file, config::DOC_BRANCH).and_then(|f| f.as_str())
        })
        .unwrap_or("gh-pages");
    let doc_commit_msg = config::get_release_config(&cargo_file, config::DOC_COMMIT_MESSAGE)
        .and_then(|f| f.as_str())
        .unwrap_or("(cargo-gh-pages) Generate docs.");

    // Check if working directory is clean.
    if !try!(git::status()) {
        println!("Uncommitted changes detected, please commit before release");
        if !dry_run {
            return Ok(101);
        }
    }

    // Generate and upload documentation.
    println!("Building and exporting docs.");
    try!(cargo::doc(dry_run));

    let doc_path = "target/doc/";

    try!(git::init(doc_path, dry_run));
    try!(git::add_all(doc_path, dry_run));
    try!(git::commit_all(doc_path, doc_commit_msg, sign, dry_run));
    let remote = try!(git::remote_get_url(git_remote));

    let mut refspec = String::from("master:");
    refspec.push_str(doc_branch);

    try!(git::force_push(doc_path, remote.trim(), &refspec, dry_run));

    Ok(0)
}

static USAGE: &'static str = "[sign]... --sign 'Sign git commit'
                             [dry-run]... --dry-run 'Do not actually change anything.'
                             --push-remote=[push-remote] 'Git remote to push'
                             --doc-branch=[doc-branch] 'Git branch to push documentation on' ";

fn main() {
    let matches = App::new("cargo")
        .subcommand(SubCommand::with_name("gh-pages")
            .version(env!("CARGO_PKG_VERSION"))
            .author("Ning Sun <sunng@about.me>")
            .author("Woof Woof, Inc.")
            .about("Cargo subcommand for generating and publishing RustDoc to GitHub Pages.")
            .args_from_usage(USAGE))
        .get_matches();

    if let Some(release_matches) = matches.subcommand_matches("gh-pages") {
        match execute(release_matches) {
            Ok(code) => exit(code),
            Err(e) => {
                println!("Fatal: {}", e);
                exit(128);
            }
        }
    }
}
<|endoftext|>


<commit_before>use std::io::{self, Write};
use std::f32::consts::PI;
use std::f32::INFINITY;

#[derive(Copy, Clone, Debug)]
pub struct Position(f32, f32);

impl Position {
    pub fn origin() -> Position {
        Position(0.0, 0.0)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Degree(pub f32);

#[derive(Copy, Clone, Debug)]
pub struct Radiant(pub f32);

impl Into<Degree> for Radiant {
    fn into(self) -> Degree {
        Degree(self.0 * 180.0 / PI)
    }
}

impl Into<Radiant> for Degree {
    fn into(self) -> Radiant {
        Radiant(self.0 * PI / 180.0)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Distance(f32);

impl Into<Distance> for f32 {
    fn into(self) -> Distance {
        Distance(self)
    }
}

pub trait Turtle {
    /// Move turtle forward by specified `distance`.
    fn forward<T: Into<Distance>>(&mut self, distance: T);

    /// Move turtle backward by specified `distance`.
    fn backward<T: Into<Distance>>(&mut self, distance: T);

    /// Turn turtle right by `angle` degree.
    fn right<T: Into<Degree>>(&mut self, angle: T);

    /// Turn turtle left by `angle` degree.
    fn left<T: Into<Degree>>(&mut self, angle: T);

    /// Put the pen down.
    fn pendown(&mut self);

    /// Put the pen up.
    fn penup(&mut self);

    fn goto(&mut self, pos: Position);

    fn home(&mut self) {
        self.goto(Position::origin());
    }

    /// Push current turtle state on stack.
    fn push(&mut self);

    /// Restore previously saved turtle state.
    fn pop(&mut self);
}

#[derive(Clone)]
pub struct TurtleState {
    pos: Position,
    angle: Degree,
    pendown: bool,
}

impl TurtleState {
    fn new() -> TurtleState {
        TurtleState {
            pos: Position::origin(),
            angle: Degree(0.0),
            pendown: false,
        }
    }
}

pub struct TurtleRecorder {
    states: Vec<TurtleState>,
    lines: Vec<(Position, Position)>,
}

impl TurtleRecorder {
    pub fn new() -> TurtleRecorder {
        TurtleRecorder {
            states: vec![TurtleState::new()],
            lines: vec![],
        }
    }

    #[inline]
    fn current_state_mut(&mut self) -> &mut TurtleState {
        self.states.last_mut().unwrap()
    }

    #[inline]
    fn current_state(&self) -> &TurtleState {
        self.states.last().unwrap()
    }

    #[inline]
    fn direction(&self, distance: Distance) -> (f32, f32) {
        let state = self.current_state();
        let rad: Radiant = state.angle.into();
        let dx = rad.0.cos() * distance.0;
        let dy = rad.0.sin() * distance.0;
        (dx, dy)
    }

    fn draw_line(&mut self, src: Position, dst: Position) {
        self.lines.push((src, dst));
    }

    /// Saves the turtle graphic as Scalable Vector Graphic (SVG).
    pub fn save_as_svg<W: Write>(&self, wr: &mut W) -> io::Result<()> {
        // Determine extend of canvas
        let mut min = Position(INFINITY, INFINITY);
        let mut max = Position(-INFINITY, -INFINITY);
        for &(ref src, ref dst) in self.lines.iter() {
            if src.0 < min.0 {
                min.0 = src.0;
            }
            if dst.0 < min.0 {
                min.0 = dst.0;
            }
            if src.0 > max.0 {
                max.0 = src.0;
            }
            if dst.0 > max.0 {
                max.0 = dst.0;
            }

            if src.1 < min.1 {
                min.1 = src.1;
            }
            if dst.1 < min.1 {
                min.1 = dst.1;
            }
            if src.1 > max.1 {
                max.1 = src.1;
            }
            if dst.1 > max.1 {
                max.1 = dst.1;
            }
        }
        let width = (max.0 - min.0).abs();
        let height = (max.1 - min.1).abs();

        let top_left = Position(min.0 - width / 10.0, min.1 - height / 10.0);
        let bottom_right = Position(max.0 + width / 10.0, max.1 + height / 10.0);

        try!(writeln!(wr,
                      r#"<?xml version="1.0" encoding="UTF-8"?>
                <svg xmlns="http://www.w3.org/2000/svg"
                version="1.1" baseProfile="full"
                width="100%" height="100%"
                viewBox="{} {} {} {}">"#,
                      top_left.0,
                      top_left.1,
                      bottom_right.0,
                      bottom_right.1));

        for &(ref src, ref dst) in self.lines.iter() {
            try!(writeln!(wr,
                          r#"<path d="M{} {} L{} {}" stroke="black" stroke-width="1"/>"#,
                          src.0,
                          src.1,
                          dst.0,
                          dst.1));
        }

        writeln!(wr, "</svg>")
    }
}

impl Turtle for TurtleRecorder {
    /// Move turtle forward by specified `distance`.
    fn forward<T: Into<Distance>>(&mut self, distance: T) {
        let (dx, dy) = self.direction(distance.into());
        let pendown = self.current_state().pendown;
        let src: Position = self.current_state().pos;
        let dst = Position(src.0 + dx, src.1 + dy);
        if pendown {
            self.draw_line(src, dst);
        }
        self.current_state_mut().pos = dst;
    }

    /// Move turtle backward by specified `distance`.
    fn backward<T: Into<Distance>>(&mut self, distance: T) {
        let distance: Distance = distance.into();
        self.forward(Distance(-distance.0))
    }

    /// Turn turtle right by `angle` degrees.
    fn right<T: Into<Degree>>(&mut self, angle: T) {
        let angle: Degree = angle.into();
        self.current_state_mut().angle.0 += angle.0;
    }

    /// Turn turtle left by `angle` degrees.
    fn left<T: Into<Degree>>(&mut self, angle: T) {
        let angle: Degree = angle.into();
        self.current_state_mut().angle.0 -= angle.0;
    }

    /// Put the pen down.
    fn pendown(&mut self) {
        self.current_state_mut().pendown = true;
    }

    /// Put the pen up.
    fn penup(&mut self) {
        self.current_state_mut().pendown = false;
    }

    fn goto(&mut self, pos: Position) {
        self.current_state_mut().pos = pos;
    }

    /// Push current turtle state on stack.
    fn push(&mut self) {
        let state = self.current_state_mut().clone();
        self.states.push(state);
    }

    /// Restore previously saved turtle state.
    fn pop(&mut self) {
        self.states.pop();
    }
}

fn main() {
    use std::fs::File;
    let mut t = TurtleRecorder::new();
    t.pendown();
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.save_as_svg(&mut File::create("test.svg").unwrap());
}
<commit_msg>Use f32::min() and f32::max()<commit_after>use std::io::{self, Write};
use std::f32::consts::PI;
use std::f32::INFINITY;

#[derive(Copy, Clone, Debug)]
pub struct Position(f32, f32);

impl Position {
    pub fn origin() -> Position {
        Position(0.0, 0.0)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Degree(pub f32);

#[derive(Copy, Clone, Debug)]
pub struct Radiant(pub f32);

impl Into<Degree> for Radiant {
    fn into(self) -> Degree {
        Degree(self.0 * 180.0 / PI)
    }
}

impl Into<Radiant> for Degree {
    fn into(self) -> Radiant {
        Radiant(self.0 * PI / 180.0)
    }
}

#[derive(Copy, Clone, Debug)]
pub struct Distance(f32);

impl Into<Distance> for f32 {
    fn into(self) -> Distance {
        Distance(self)
    }
}

pub trait Turtle {
    /// Move turtle forward by specified `distance`.
    fn forward<T: Into<Distance>>(&mut self, distance: T);

    /// Move turtle backward by specified `distance`.
    fn backward<T: Into<Distance>>(&mut self, distance: T);

    /// Turn turtle right by `angle` degree.
    fn right<T: Into<Degree>>(&mut self, angle: T);

    /// Turn turtle left by `angle` degree.
    fn left<T: Into<Degree>>(&mut self, angle: T);

    /// Put the pen down.
    fn pendown(&mut self);

    /// Put the pen up.
    fn penup(&mut self);

    fn goto(&mut self, pos: Position);

    fn home(&mut self) {
        self.goto(Position::origin());
    }

    /// Push current turtle state on stack.
    fn push(&mut self);

    /// Restore previously saved turtle state.
    fn pop(&mut self);
}

#[derive(Clone)]
pub struct TurtleState {
    pos: Position,
    angle: Degree,
    pendown: bool,
}

impl TurtleState {
    fn new() -> TurtleState {
        TurtleState {
            pos: Position::origin(),
            angle: Degree(0.0),
            pendown: false,
        }
    }
}

pub struct TurtleRecorder {
    states: Vec<TurtleState>,
    lines: Vec<(Position, Position)>,
}

impl TurtleRecorder {
    pub fn new() -> TurtleRecorder {
        TurtleRecorder {
            states: vec![TurtleState::new()],
            lines: vec![],
        }
    }

    #[inline]
    fn current_state_mut(&mut self) -> &mut TurtleState {
        self.states.last_mut().unwrap()
    }

    #[inline]
    fn current_state(&self) -> &TurtleState {
        self.states.last().unwrap()
    }

    #[inline]
    fn direction(&self, distance: Distance) -> (f32, f32) {
        let state = self.current_state();
        let rad: Radiant = state.angle.into();
        let dx = rad.0.cos() * distance.0;
        let dy = rad.0.sin() * distance.0;
        (dx, dy)
    }

    fn draw_line(&mut self, src: Position, dst: Position) {
        self.lines.push((src, dst));
    }

    /// Saves the turtle graphic as Scalable Vector Graphic (SVG).
    pub fn save_as_svg<W: Write>(&self, wr: &mut W) -> io::Result<()> {
        // Determine extend of canvas
        let mut min = Position(INFINITY, INFINITY);
        let mut max = Position(-INFINITY, -INFINITY);
        for &(ref src, ref dst) in self.lines.iter() {
            min.0 = min.0.min(src.0).min(dst.0);
            max.0 = max.0.max(src.0).max(dst.0);

            min.1 = min.1.min(src.1).min(dst.1);
            max.1 = max.1.max(src.1).max(dst.1);

        }
        let width = (max.0 - min.0).abs();
        let height = (max.1 - min.1).abs();

        let top_left = Position(min.0 - width / 10.0, min.1 - height / 10.0);
        let bottom_right = Position(max.0 + width / 10.0, max.1 + height / 10.0);

        try!(writeln!(wr,
                      r#"<?xml version="1.0" encoding="UTF-8"?>
                <svg xmlns="http://www.w3.org/2000/svg"
                version="1.1" baseProfile="full"
                width="100%" height="100%"
                viewBox="{} {} {} {}">"#,
                      top_left.0,
                      top_left.1,
                      bottom_right.0,
                      bottom_right.1));

        for &(ref src, ref dst) in self.lines.iter() {
            try!(writeln!(wr,
                          r#"<path d="M{} {} L{} {}" stroke="black" stroke-width="1"/>"#,
                          src.0,
                          src.1,
                          dst.0,
                          dst.1));
        }

        writeln!(wr, "</svg>")
    }
}

impl Turtle for TurtleRecorder {
    /// Move turtle forward by specified `distance`.
    fn forward<T: Into<Distance>>(&mut self, distance: T) {
        let (dx, dy) = self.direction(distance.into());
        let pendown = self.current_state().pendown;
        let src: Position = self.current_state().pos;
        let dst = Position(src.0 + dx, src.1 + dy);
        if pendown {
            self.draw_line(src, dst);
        }
        self.current_state_mut().pos = dst;
    }

    /// Move turtle backward by specified `distance`.
    fn backward<T: Into<Distance>>(&mut self, distance: T) {
        let distance: Distance = distance.into();
        self.forward(Distance(-distance.0))
    }

    /// Turn turtle right by `angle` degrees.
    fn right<T: Into<Degree>>(&mut self, angle: T) {
        let angle: Degree = angle.into();
        self.current_state_mut().angle.0 += angle.0;
    }

    /// Turn turtle left by `angle` degrees.
    fn left<T: Into<Degree>>(&mut self, angle: T) {
        let angle: Degree = angle.into();
        self.current_state_mut().angle.0 -= angle.0;
    }

    /// Put the pen down.
    fn pendown(&mut self) {
        self.current_state_mut().pendown = true;
    }

    /// Put the pen up.
    fn penup(&mut self) {
        self.current_state_mut().pendown = false;
    }

    fn goto(&mut self, pos: Position) {
        self.current_state_mut().pos = pos;
    }

    /// Push current turtle state on stack.
    fn push(&mut self) {
        let state = self.current_state_mut().clone();
        self.states.push(state);
    }

    /// Restore previously saved turtle state.
    fn pop(&mut self) {
        self.states.pop();
    }
}

fn main() {
    use std::fs::File;
    let mut t = TurtleRecorder::new();
    t.pendown();
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.right(Degree(90.0));
    t.forward(10.0);
    t.save_as_svg(&mut File::create("test.svg").unwrap());
}
<|endoftext|>


<commit_before>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

extern crate aggregated_stats;

#[macro_use]
extern crate clap;

extern crate env_logger;
#[macro_use]
extern crate log;

extern crate prometheus;
extern crate hyper;

mod analyzer;
mod args;
mod filter;
mod log_parser;
mod render;
mod request_response_matcher;
mod http_handler;
mod result;

fn main() {
    env_logger::init().expect("Failed to initialize logging.");

    let args = args::parse_args(env::args()).expect("Failed to parse arguments.");

    let result = run(&args);

    let mut stream;
    let mut stdout;

    let mut renderers: Vec<Box<render::Renderer>>;
    renderers = vec![];

    if !args.quiet {
        stdout = io::stdout();
        renderers.push(Box::new(render::terminal::TerminalRenderer::new(&mut stdout)));
    }

    if args.graphite_server.is_some() {
        stream = TcpStream::connect((args.graphite_server.as_ref().unwrap().as_str(),
                                     args.graphite_port.unwrap()))
            .expect("Could not connect to the Graphite server");

        renderers.push(Box::new(render::graphite::GraphiteRenderer::new(Utc::now(),
                                                                        args.graphite_prefix
                                                                            .clone(),
                                                                        &mut stream)));
    }

    if args.influxdb_write_url.is_some() {
        renderers.push(Box::new(render::influxdb::InfluxDbRenderer::new(&args.influxdb_write_url
                                                                            .clone()
                                                                            .unwrap(),
                                                                        args.influxdb_tags
                                                                            .clone())));
    }

    for mut renderer in renderers {
        renderer.render(result.clone());
    }

    if args.prometheus_listen.is_some() {
        let binding_address = args.prometheus_listen.clone().unwrap();
        http_handler::listen_http(args, &binding_address);
    }
}

fn run(args: &args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult {
    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).expect("Failed to open file.")),
    };

    let reader = io::BufReader::new(input);

    let mut events_iterator = reader.lines()
        .map(log_parser::parse_line)
        .filter(|event| event.is_ok())
        .map(|event| event.unwrap());

    let pairs_iterator =
        request_response_matcher::RequestResponsePairIterator::new(&mut events_iterator)
            .filter(|pair| filter::matches_filter(pair, &args.conditions));

    analyzer::analyze_iterator(pairs_iterator)
}

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

    #[test]
    fn test_run() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/simple-1.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
            influxdb_write_url: None,
            influxdb_tags: None,
            quiet: false,
        };

        let result = run(&args);
        assert_eq!(result.count, 2);

        let timing = result.timing.unwrap();
        assert_eq!(timing.min, 7);
        assert_eq!(timing.max, 10);

        assert!(result.error.is_some());
    }

    #[test]
    fn test_run_ignore_broken_lines() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/broken.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
            influxdb_write_url: None,
            influxdb_tags: None,
            quiet: false,
        };

        let result = run(&args);
        assert_eq!(result.count, 1);
    }
}
<commit_msg>Cleanly separate Prometheus server mode<commit_after>use std::io;
use std::io::prelude::*;
use std::net::TcpStream;
use std::fs::File;
use std::env;

extern crate chrono;
use chrono::*;

extern crate aggregated_stats;

#[macro_use]
extern crate clap;

extern crate env_logger;
#[macro_use]
extern crate log;

extern crate prometheus;
extern crate hyper;

mod analyzer;
mod args;
mod filter;
mod log_parser;
mod render;
mod request_response_matcher;
mod http_handler;
mod result;

fn main() {
    env_logger::init().expect("Failed to initialize logging.");

    let args = args::parse_args(env::args()).expect("Failed to parse arguments.");

    if args.prometheus_listen.is_some() {
        let binding_address = args.prometheus_listen.clone().unwrap();
        http_handler::listen_http(args, &binding_address);
    } else {
        let result = run(&args);

        let mut stream;
        let mut stdout;

        let mut renderers: Vec<Box<render::Renderer>>;
        renderers = vec![];

        if !args.quiet {
            stdout = io::stdout();
            renderers.push(Box::new(render::terminal::TerminalRenderer::new(&mut stdout)));
        }

        if args.graphite_server.is_some() {
            stream = TcpStream::connect((args.graphite_server.as_ref().unwrap().as_str(),
                                         args.graphite_port.unwrap()))
                .expect("Could not connect to the Graphite server");

            renderers.push(Box::new(render::graphite::GraphiteRenderer::new(Utc::now(),
                                                                            args.graphite_prefix
                                                                                .clone(),
                                                                            &mut stream)));
        }

        if args.influxdb_write_url.is_some() {
            renderers.push(Box::new(render::influxdb::InfluxDbRenderer::new(&args.influxdb_write_url
                                                                           .clone()
                                                                           .unwrap(),
                                                                       args.influxdb_tags
                                                                           .clone())));
        }

        for mut renderer in renderers {
            renderer.render(result.clone());
        }
    }
}

fn run(args: &args::RequestLogAnalyzerArgs) -> result::RequestLogAnalyzerResult {
    let input: Box<io::Read> = match args.filename.as_ref() {
        "-" => Box::new(io::stdin()),
        _ => Box::new(File::open(&args.filename).expect("Failed to open file.")),
    };

    let reader = io::BufReader::new(input);

    let mut events_iterator = reader.lines()
        .map(log_parser::parse_line)
        .filter(|event| event.is_ok())
        .map(|event| event.unwrap());

    let pairs_iterator =
        request_response_matcher::RequestResponsePairIterator::new(&mut events_iterator)
            .filter(|pair| filter::matches_filter(pair, &args.conditions));

    analyzer::analyze_iterator(pairs_iterator)
}

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

    #[test]
    fn test_run() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/simple-1.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
            influxdb_write_url: None,
            influxdb_tags: None,
            quiet: false,
        };

        let result = run(&args);
        assert_eq!(result.count, 2);

        let timing = result.timing.unwrap();
        assert_eq!(timing.min, 7);
        assert_eq!(timing.max, 10);

        assert!(result.error.is_some());
    }

    #[test]
    fn test_run_ignore_broken_lines() {
        let args = args::RequestLogAnalyzerArgs {
            filename: String::from("src/test/broken.log"),
            conditions: filter::FilterConditions {
                include_terms: None,
                exclude_terms: None,
                latest_time: None,
            },
            graphite_server: None,
            graphite_port: Some(2003),
            graphite_prefix: None,
            prometheus_listen: None,
            influxdb_write_url: None,
            influxdb_tags: None,
            quiet: false,
        };

        let result = run(&args);
        assert_eq!(result.count, 1);
    }
}
<|endoftext|>