<commit_before>use super::page::*;
use maud::{html, Markup};
use rocket::request::FlashMessage;

pub fn reset_password_request(flash: Option<FlashMessage>, action: &str) -> Markup {
    page(
        "Request password reset",
        None,
        flash,
        None,
        html! {
          h2 { "Password reset" }
          p { "Please enter your email address to reset your password." }
          form class="reset-pw-req" action=(action) method="POST" {
              fieldset{
                label {
                    "eMail:"
                    br;
                    input type="text" name="email" placeholder="eMail address";
                }
                br;
                input type="submit" value="reset";
              }
          }
        },
    )
}

pub fn reset_password_request_ack(flash: Option<FlashMessage>) -> Markup {
    page(
        "Request password reset",
        None,
        flash,
        None,
        html! {
          h2 { "Password reset" }
          p {
            "Your request to reset your password was successfully send."
            br;
            "Please look into your email inbox to continue."
          }
        },
    )
}

pub fn reset_password(flash: Option<FlashMessage>, action: &str, token: &str) -> Markup {
    page(
        "New password",
        None,
        flash,
        None,
        html! {
          form class="reset-pw" action=(action) method="POST" {
              fieldset{
                label {
                    "eMail:"
                    br;
                    input type="text" name="email" placeholder="eMail address";
                }
                br;
                label{
                    "New password:"
                    br;
                    input type="password" name="new_password" placeholder="new password";
                }
                br;
                label{
                    "New password (repeated):"
                    br;
                    input type="password" name="new_password_repeated" placeholder="repeat new password";
                }
                br;
                input type="hidden" name="token" value=(token);
                input type="submit" value="save";
              }
          }
        },
    )
}

pub fn reset_password_ack(flash: Option<FlashMessage>) -> Markup {
    page(
        "New password",
        None,
        flash,
        None,
        html! {
          h2 { "New password" }
          p { "Your password changed successfully." }
          p { a href = "/login" { "Login with your new password."} }
        },
    )
}
<commit_msg>Delete obsolete form field when resetting password<commit_after>use super::page::*;
use maud::{html, Markup};
use rocket::request::FlashMessage;

pub fn reset_password_request(flash: Option<FlashMessage>, action: &str) -> Markup {
    page(
        "Request password reset",
        None,
        flash,
        None,
        html! {
          h2 { "Password reset" }
          p { "Please enter your email address to reset your password." }
          form class="reset-pw-req" action=(action) method="POST" {
              fieldset{
                label {
                    "eMail:"
                    br;
                    input type="text" name="email" placeholder="eMail address";
                }
                br;
                input type="submit" value="reset";
              }
          }
        },
    )
}

pub fn reset_password_request_ack(flash: Option<FlashMessage>) -> Markup {
    page(
        "Request password reset",
        None,
        flash,
        None,
        html! {
          h2 { "Password reset" }
          p {
            "Your request to reset your password was successfully send."
            br;
            "Please look into your email inbox to continue."
          }
        },
    )
}

pub fn reset_password(flash: Option<FlashMessage>, action: &str, token: &str) -> Markup {
    page(
        "New password",
        None,
        flash,
        None,
        html! {
          form class="reset-pw" action=(action) method="POST" {
              fieldset{
                label{
                    "New password:"
                    br;
                    input type="password" name="new_password" placeholder="new password";
                }
                br;
                label{
                    "New password (repeated):"
                    br;
                    input type="password" name="new_password_repeated" placeholder="repeat new password";
                }
                br;
                input type="hidden" name="token" value=(token);
                input type="submit" value="save";
              }
          }
        },
    )
}

pub fn reset_password_ack(flash: Option<FlashMessage>) -> Markup {
    page(
        "New password",
        None,
        flash,
        None,
        html! {
          h2 { "New password" }
          p { "Your password changed successfully." }
          p { a href = "/login" { "Login with your new password."} }
        },
    )
}
<|endoftext|>


<commit_before>use std::env;
use std::rc::Rc;
use std::cell::RefCell;
use sfml::graphics::{RenderWindow, RenderTarget, Font};
use sfml::window::{ContextSettings, VideoMode, Close};
use sfml::window::event;
use sfml::window::keyboard::Key;
use sfml::window::mouse::MouseButton;
use sfml::graphics::Color;
use sfml::system::vector2::Vector2i;
use na::{Pnt2, Pnt3, Iso2};
use na;
use nphysics::world::World;
use nphysics::object::RigidBody;
use nphysics::detection::joint::{Fixed, Anchor};
use camera::Camera;
use fps::Fps;
use engine::GraphicsManager;
use draw_helper;

fn usage(exe_name: &str) {
    println!("Usage: {} [OPTION] ", exe_name);
    println!("");
    println!("Options:");
    println!("    --help  - prints this help message and exits.");
    println!("    --pause - do not start the simulation right away.");
    println!("");
    println!("The following keyboard commands are supported:");
    println!("    t     - pause/continue the simulation.");
    println!("    s     - pause then execute only one simulation step.");
    println!("    space - display/hide contacts.");
}

#[derive(PartialEq)]
enum RunMode {
    Running,
    Stop,
    Step
}

pub struct Testbed<'a> {
    world:    World,
    window:   RenderWindow,
    graphics: GraphicsManager<'a>
}

struct TestbedState<'a> {
    running: RunMode,
    draw_colls: bool,
    camera: Camera,
    fps: Fps<'a>,
    grabbed_object: Option<Rc<RefCell<RigidBody>>>,
    grabbed_object_joint: Option<Rc<RefCell<Fixed>>>,
}

impl<'a> TestbedState<'a> {
    fn new(fnt: &'a Font) -> TestbedState<'a> {
        TestbedState{
            running: RunMode::Running,
            draw_colls: false,
            camera: Camera::new(),
            fps: Fps::new(&fnt),
            grabbed_object: None,
            grabbed_object_joint: None,
        }
    }
}

impl<'a> Testbed<'a> {
    pub fn new_empty() -> Testbed<'a> {
        let mode    = VideoMode::new_init(800, 600, 32);
        let setting = ContextSettings {
            depth_bits:         10,
            stencil_bits:       10,
            antialiasing_level: 2,
            major_version:      0,
            minor_version:      1
        };
        let window =
            match RenderWindow::new(mode, "nphysics 2d demo", Close, &setting) {
                Some(rwindow) => rwindow,
                None          => panic!("Error on creating the sfml window.")
            };
        let graphics = GraphicsManager::new();

        Testbed {
            world:    World::new(),
            window:   window,
            graphics: graphics
        }
    }

    pub fn new(world: World) -> Testbed<'a> {
        let mut res = Testbed::new_empty();

        res.set_world(world);

        res
    }

    pub fn set_world(&mut self, world: World) {
        self.world = world;
        self.graphics.clear();

        for rb in self.world.bodies() {
            self.graphics.add(rb.clone());
        }
    }

    pub fn set_color(&mut self, body: &Rc<RefCell<RigidBody>>, color: Pnt3<f32>) {
        let color = Pnt3::new(
            (color.x * 255.0) as u8,
            (color.y * 255.0) as u8,
            (color.z * 255.0) as u8
        );

        self.graphics.set_color(body, color);
    }

    pub fn run(&mut self) {
        let font_mem = include_bytes!("Inconsolata.otf");
        let     fnt  = Font::new_from_memory(font_mem).unwrap();

        let mut state = TestbedState::new(&fnt);

        let mut args    = env::args();

        if args.len() > 1 {
            let exname = args.next().unwrap();
            for arg in args {
                if &arg[..] == "--help" || &arg[..] == "-h" {
                    usage(&exname[..]);
                    return;
                }
                else if &arg[..] == "--pause" {
                    state.running = RunMode::Stop;
                }
            }
        }

        self.window.set_framerate_limit(60);

        self.foo(state);

        self.window.close();
    }

    fn foo(&mut self, mut state: TestbedState) {
        while self.window.is_open() {
            self.foo_loop(&mut state);

            self.window.clear(&Color::black());

            state.fps.reset();

            if state.running != RunMode::Stop {
                self.world.step(0.016);
            }

            if state.running == RunMode::Step {
                state.running = RunMode::Stop;
            }
            state.fps.register_delta();
            self.graphics.draw(&mut self.window, &state.camera);

            state.camera.activate_scene(&mut self.window);
            if state.draw_colls {
                draw_helper::draw_colls(&mut self.window, &mut self.world);
            }

            state.camera.activate_ui(&mut self.window);
            state.fps.draw_registered(&mut self.window);

            self.window.display();
        }
    }

    fn foo_loop(&mut self, mut state: &mut TestbedState) {
        loop {
            match self.window.poll_event() {
                event::KeyPressed{code, ..} => self.process_key_press(&mut state, code),
                event::MouseButtonPressed{button, x, y} => self.process_mouse_press(&mut state, button, x, y),
                event::MouseButtonReleased{button, x, y} => {
                    match button {
                        MouseButton::MouseLeft => {
                            match state.grabbed_object {
                                Some(ref b) => {
                                    for node in self.graphics.body_to_scene_node(b).unwrap().iter_mut() {
                                        node.unselect()
                                    }
                                },
                                None => { }
                            }

                            match state.grabbed_object_joint {
                                Some(ref j) => self.world.remove_fixed(j),
                                None => { }
                            }

                            state.grabbed_object = None;
                            state.grabbed_object_joint = None;
                        },
                        _ => {
                            state.camera.handle_event(&event::MouseButtonReleased{ button: button, x: x, y: y })
                        }
                    }
                }
                event::MouseMoved{x, y} => {
                    let mapped_coords = state.camera.map_pixel_to_coords(Vector2i::new(x, y));
                    let mapped_point = Pnt2::new(mapped_coords.x, mapped_coords.y);
                    let _1: Iso2<f32> = na::one();
                    let attach2 = na::append_translation(&_1, (mapped_point).as_vec());
                    match state.grabbed_object {
                        Some(_) => {
                            let joint = state.grabbed_object_joint.as_ref().unwrap();
                            joint.borrow_mut().set_local2(attach2);
                        },
                        None => state.camera.handle_event(&event::MouseMoved{x: x, y: y})
                    };
                },
                event::Closed  => self.window.close(),
                event::NoEvent => break,
                e              => state.camera.handle_event(&e)
            }
        }
    }

    fn process_key_press(&mut self, state: &mut TestbedState, code: Key) {
        match code {
            Key::Escape => self.window.close(),
            Key::S      => state.running = RunMode::Step,
            Key::Space  => state.draw_colls = !state.draw_colls,
            Key::T      => {
                if state.running == RunMode::Stop {
                    state.running = RunMode::Running;
                }
                else {
                    state.running = RunMode::Stop;
                }
            },
            _                => { }
        }
    }

    fn process_mouse_press(&mut self, state: &mut TestbedState, button: MouseButton, x: i32, y: i32) {
        match button {
            MouseButton::MouseLeft => {
                let mapped_coords = state.camera.map_pixel_to_coords(Vector2i::new(x, y));
                let mapped_point = Pnt2::new(mapped_coords.x, mapped_coords.y);
                self.world.interferences_with_point(&mapped_point, |b| {
                    if b.borrow().can_move() {
                        state.grabbed_object = Some(b.clone())
                    }
                });

                match state.grabbed_object {
                    Some(ref b) => {
                        for node in self.graphics.body_to_scene_node(b).unwrap().iter_mut() {
                            match state.grabbed_object_joint {
                                Some(ref j) => self.world.remove_fixed(j),
                                None        => { }
                            }

                            let _1: Iso2<f32> = na::one();
                            let attach2 = na::append_translation(&_1, mapped_point.as_vec());
                            let attach1 = na::inv(&na::transformation(b.borrow().position())).unwrap() * attach2;
                            let anchor1 = Anchor::new(Some(state.grabbed_object.as_ref().unwrap().clone()), attach1);
                            let anchor2 = Anchor::new(None, attach2);
                            let joint = Fixed::new(anchor1, anchor2);
                            state.grabbed_object_joint = Some(self.world.add_fixed(joint));
                            node.select()
                        }
                    },
                    None => { }
                }
            },
            _ => {
                state.camera.handle_event(&event::MouseButtonPressed{ button: button, x: x, y: y })
            }
        }
    }
}
<commit_msg>Factored out mouse release<commit_after>use std::env;
use std::rc::Rc;
use std::cell::RefCell;
use sfml::graphics::{RenderWindow, RenderTarget, Font};
use sfml::window::{ContextSettings, VideoMode, Close};
use sfml::window::event;
use sfml::window::keyboard::Key;
use sfml::window::mouse::MouseButton;
use sfml::graphics::Color;
use sfml::system::vector2::Vector2i;
use na::{Pnt2, Pnt3, Iso2};
use na;
use nphysics::world::World;
use nphysics::object::RigidBody;
use nphysics::detection::joint::{Fixed, Anchor};
use camera::Camera;
use fps::Fps;
use engine::GraphicsManager;
use draw_helper;

fn usage(exe_name: &str) {
    println!("Usage: {} [OPTION] ", exe_name);
    println!("");
    println!("Options:");
    println!("    --help  - prints this help message and exits.");
    println!("    --pause - do not start the simulation right away.");
    println!("");
    println!("The following keyboard commands are supported:");
    println!("    t     - pause/continue the simulation.");
    println!("    s     - pause then execute only one simulation step.");
    println!("    space - display/hide contacts.");
}

#[derive(PartialEq)]
enum RunMode {
    Running,
    Stop,
    Step
}

pub struct Testbed<'a> {
    world:    World,
    window:   RenderWindow,
    graphics: GraphicsManager<'a>
}

struct TestbedState<'a> {
    running: RunMode,
    draw_colls: bool,
    camera: Camera,
    fps: Fps<'a>,
    grabbed_object: Option<Rc<RefCell<RigidBody>>>,
    grabbed_object_joint: Option<Rc<RefCell<Fixed>>>,
}

impl<'a> TestbedState<'a> {
    fn new(fnt: &'a Font) -> TestbedState<'a> {
        TestbedState{
            running: RunMode::Running,
            draw_colls: false,
            camera: Camera::new(),
            fps: Fps::new(&fnt),
            grabbed_object: None,
            grabbed_object_joint: None,
        }
    }
}

impl<'a> Testbed<'a> {
    pub fn new_empty() -> Testbed<'a> {
        let mode    = VideoMode::new_init(800, 600, 32);
        let setting = ContextSettings {
            depth_bits:         10,
            stencil_bits:       10,
            antialiasing_level: 2,
            major_version:      0,
            minor_version:      1
        };
        let window =
            match RenderWindow::new(mode, "nphysics 2d demo", Close, &setting) {
                Some(rwindow) => rwindow,
                None          => panic!("Error on creating the sfml window.")
            };
        let graphics = GraphicsManager::new();

        Testbed {
            world:    World::new(),
            window:   window,
            graphics: graphics
        }
    }

    pub fn new(world: World) -> Testbed<'a> {
        let mut res = Testbed::new_empty();

        res.set_world(world);

        res
    }

    pub fn set_world(&mut self, world: World) {
        self.world = world;
        self.graphics.clear();

        for rb in self.world.bodies() {
            self.graphics.add(rb.clone());
        }
    }

    pub fn set_color(&mut self, body: &Rc<RefCell<RigidBody>>, color: Pnt3<f32>) {
        let color = Pnt3::new(
            (color.x * 255.0) as u8,
            (color.y * 255.0) as u8,
            (color.z * 255.0) as u8
        );

        self.graphics.set_color(body, color);
    }

    pub fn run(&mut self) {
        let font_mem = include_bytes!("Inconsolata.otf");
        let     fnt  = Font::new_from_memory(font_mem).unwrap();

        let mut state = TestbedState::new(&fnt);

        let mut args    = env::args();

        if args.len() > 1 {
            let exname = args.next().unwrap();
            for arg in args {
                if &arg[..] == "--help" || &arg[..] == "-h" {
                    usage(&exname[..]);
                    return;
                }
                else if &arg[..] == "--pause" {
                    state.running = RunMode::Stop;
                }
            }
        }

        self.window.set_framerate_limit(60);

        self.foo(state);

        self.window.close();
    }

    fn foo(&mut self, mut state: TestbedState) {
        while self.window.is_open() {
            self.foo_loop(&mut state);

            self.window.clear(&Color::black());

            state.fps.reset();

            if state.running != RunMode::Stop {
                self.world.step(0.016);
            }

            if state.running == RunMode::Step {
                state.running = RunMode::Stop;
            }
            state.fps.register_delta();
            self.graphics.draw(&mut self.window, &state.camera);

            state.camera.activate_scene(&mut self.window);
            if state.draw_colls {
                draw_helper::draw_colls(&mut self.window, &mut self.world);
            }

            state.camera.activate_ui(&mut self.window);
            state.fps.draw_registered(&mut self.window);

            self.window.display();
        }
    }

    fn foo_loop(&mut self, mut state: &mut TestbedState) {
        loop {
            match self.window.poll_event() {
                event::KeyPressed{code, ..} => self.process_key_press(&mut state, code),
                event::MouseButtonPressed{button, x, y} => self.process_mouse_press(&mut state, button, x, y),
                event::MouseButtonReleased{button, x, y} => self.process_mouse_release(&mut state, button, x, y),
                event::MouseMoved{x, y} => {
                    let mapped_coords = state.camera.map_pixel_to_coords(Vector2i::new(x, y));
                    let mapped_point = Pnt2::new(mapped_coords.x, mapped_coords.y);
                    let _1: Iso2<f32> = na::one();
                    let attach2 = na::append_translation(&_1, (mapped_point).as_vec());
                    match state.grabbed_object {
                        Some(_) => {
                            let joint = state.grabbed_object_joint.as_ref().unwrap();
                            joint.borrow_mut().set_local2(attach2);
                        },
                        None => state.camera.handle_event(&event::MouseMoved{x: x, y: y})
                    };
                },
                event::Closed  => self.window.close(),
                event::NoEvent => break,
                e              => state.camera.handle_event(&e)
            }
        }
    }

    fn process_key_press(&mut self, state: &mut TestbedState, code: Key) {
        match code {
            Key::Escape => self.window.close(),
            Key::S      => state.running = RunMode::Step,
            Key::Space  => state.draw_colls = !state.draw_colls,
            Key::T      => {
                if state.running == RunMode::Stop {
                    state.running = RunMode::Running;
                }
                else {
                    state.running = RunMode::Stop;
                }
            },
            _                => { }
        }
    }

    fn process_mouse_press(&mut self, state: &mut TestbedState, button: MouseButton, x: i32, y: i32) {
        match button {
            MouseButton::MouseLeft => {
                let mapped_coords = state.camera.map_pixel_to_coords(Vector2i::new(x, y));
                let mapped_point = Pnt2::new(mapped_coords.x, mapped_coords.y);
                self.world.interferences_with_point(&mapped_point, |b| {
                    if b.borrow().can_move() {
                        state.grabbed_object = Some(b.clone())
                    }
                });

                match state.grabbed_object {
                    Some(ref b) => {
                        for node in self.graphics.body_to_scene_node(b).unwrap().iter_mut() {
                            match state.grabbed_object_joint {
                                Some(ref j) => self.world.remove_fixed(j),
                                None        => { }
                            }

                            let _1: Iso2<f32> = na::one();
                            let attach2 = na::append_translation(&_1, mapped_point.as_vec());
                            let attach1 = na::inv(&na::transformation(b.borrow().position())).unwrap() * attach2;
                            let anchor1 = Anchor::new(Some(state.grabbed_object.as_ref().unwrap().clone()), attach1);
                            let anchor2 = Anchor::new(None, attach2);
                            let joint = Fixed::new(anchor1, anchor2);
                            state.grabbed_object_joint = Some(self.world.add_fixed(joint));
                            node.select()
                        }
                    },
                    None => { }
                }
            },
            _ => {
                state.camera.handle_event(&event::MouseButtonPressed{ button: button, x: x, y: y })
            }
        }
    }

    fn process_mouse_release(&mut self, state: &mut TestbedState, button: MouseButton, x: i32, y: i32) {
        match button {
            MouseButton::MouseLeft => {
                match state.grabbed_object {
                    Some(ref b) => {
                        for node in self.graphics.body_to_scene_node(b).unwrap().iter_mut() {
                            node.unselect()
                        }
                    },
                    None => { }
                }

                match state.grabbed_object_joint {
                    Some(ref j) => self.world.remove_fixed(j),
                    None => { }
                }

                state.grabbed_object = None;
                state.grabbed_object_joint = None;
            },
            _ => {
                state.camera.handle_event(&event::MouseButtonReleased{ button: button, x: x, y: y })
            }
        }
    }
}
<|endoftext|>


<commit_before>#[deriving(Eq)]
#[deriving(Clone)]
pub enum Value {
	List(~[Value]),
	Atom(~str),
	Str(~str),
	Num(float)
}

#[inline]
fn isws(c: char) -> bool {
	c == ' ' || c == '\t' || c == '\n'
}

fn read_nows(input: &str, start: uint) -> Option<char> {
	input.iter().skip(start).skip_while(|&c| isws(c)).next()
}

fn read_number(input: &str, start: uint) -> Option<(~Value, uint)> {
	let end = input.iter().skip(start).position(|c| isws(c) || c == ')');
	let pos = match end {
		Some(end) => start+end,
		None => input.len()
	};
	let acc = input.slice(start, pos);
	match std::float::from_str(acc) {
		Some(f) => Some((~Num(f), pos)),
		None => None
	}
}

fn read_list(input: &str, start: uint) -> Option<(~Value, uint)> {
	let mut list = ~[];
	let mut i = start;
	loop {
		match read_value(input, i) {
			Some((v,new_i)) => {
				list.push(*v);
				i = new_i;
			}
			None if read_nows(input, i) == Some(')') => return Some((~List(list), i)),
			None => return None
		}
	}
}

fn read_atom(input: &str, start: uint) -> Option<(~Value, uint)> {
	let atom = std::str::from_chars(input.iter().skip(start).take_while(|&c| !isws(c) && c != ')').to_owned_vec());
	let len = atom.len();
	Some((~Atom(atom), start + len))
}

fn read_value(input: &str, start: uint) -> Option<(~Value, uint)> {
	if(start >= input.len()) {
		return None
	}

	match input[start] as char {
		c if isws(c) => read_value(input, start+1), // ignore whitespace
		'(' => read_list(input, start+1),
		')' => None,
		c if c.is_digit() || c == '.' => read_number(input, start),
		_ => read_atom(input, start)
	}
}

pub fn parse(input: &str) -> Option<Value> {
	/*do io::with_str_reader(input) |r| {
		//let c =
		println("blah: " + input + " -> " + read_token(r));
		List(~[])
	}*/
	match read_value(input, 0) {
		Some((v,_)) => Some(*v),
		None => None
	}
}

mod test {
	use super::{parse, Value, Num, List, Atom};

	fn parse_some(input: &'static str, expr: Value) {
		match parse(input) {
			Some(ref p) if p == &expr => {}
			Some(ref p) => {
				if(p != &expr) {
					fail!("Expected %?, not %?, for %?", expr, *p, input)
				}
			}
			None => fail!("Got None for %?", input)
		}
	}

	#[test]
	fn test_sexpr() {
		assert_eq!(parse(""), None);
		parse_some("123", Num(123.0));
		parse_some("()", List(~[]));
		parse_some("3.14159265358", Num(3.14159265358));
		parse_some("(hi)", List(~[Atom(~"hi")]));
		parse_some("(1)", List(~[Num(1.0)]));
		parse_some("(hi there)", List(~[Atom(~"hi"), Atom(~"there")]));
		parse_some("(hi (there))", List(~[Atom(~"hi"), List(~[Atom(~"there")])]));
		parse_some("(hi 123456)", List(~[Atom(~"hi"), Num(123456.0)]));
		parse_some("(())", List(~[List(~[])]));
		parse_some("(hi (there (fellow (human-bot!))))",
			List(~[Atom(~"hi"),
				List(~[Atom(~"there"),
					List(~[Atom(~"fellow"),
						List(~[Atom(~"human-bot!")])])])])
		);
	}

	#[test]
	fn test_whitespace() {
		// spaces
		parse_some("  (  1  )  ", List(~[Num(1.0)]));
		parse_some("  (  (  )  )  )", List(~[List(~[])]));

		// newlines
		parse_some("\n\n( \n\n )\n\n", List(~[]));
		parse_some("\n\n(\n\n123\n\n)\n\n", List(~[Num(123.0)]));
	}
}<commit_msg>cleanup<commit_after>#[deriving(Eq)]
#[deriving(Clone)]
pub enum Value {
	List(~[Value]),
	Atom(~str),
	Str(~str),
	Num(float)
}

#[inline]
fn isws(c: char) -> bool {
	c == ' ' || c == '\t' || c == '\n'
}

fn read_nows(input: &str, start: uint) -> Option<char> {
	input.iter().skip(start).skip_while(|&c| isws(c)).next()
}

fn read_number(input: &str, start: uint) -> Option<(~Value, uint)> {
	let end = input.iter().skip(start).position(|c| isws(c) || c == ')');
	let pos = match end {
		Some(end) => start+end,
		None => input.len()
	};
	let acc = input.slice(start, pos);
	match std::float::from_str(acc) {
		Some(f) => Some((~Num(f), pos)),
		None => None
	}
}

fn read_list(input: &str, start: uint) -> Option<(~Value, uint)> {
	let mut list = ~[];
	let mut i = start;
	loop {
		match read_value(input, i) {
			Some((v,new_i)) => {
				list.push(*v);
				i = new_i;
			}
			None if read_nows(input, i) == Some(')') => return Some((~List(list), i)),
			None => return None
		}
	}
}

fn read_atom(input: &str, start: uint) -> Option<(~Value, uint)> {
	let atom = std::str::from_chars(input.iter().skip(start).take_while(|&c| !isws(c) && c != ')').to_owned_vec());
	let len = atom.len();
	Some((~Atom(atom), start + len))
}

fn read_value(input: &str, start: uint) -> Option<(~Value, uint)> {
	if(start >= input.len()) {
		return None
	}

	match input[start] as char {
		c if isws(c) => read_value(input, start+1), // ignore whitespace
		'(' => read_list(input, start+1),
		')' => None,
		c if c.is_digit() || c == '.' => read_number(input, start),
		_ => read_atom(input, start)
	}
}

pub fn parse(input: &str) -> Option<Value> {
	match read_value(input, 0) {
		Some((v,_)) => Some(*v),
		None => None
	}
}

#[cfg(test)]
mod test {
	use super::{parse, Value, Num, List, Atom};

	fn parse_some(input: &'static str, expr: Value) {
		match parse(input) {
			Some(ref p) if p == &expr => {}
			Some(ref p) => {
				if(p != &expr) {
					fail!("Expected %?, not %?, for %?", expr, *p, input)
				}
			}
			None => fail!("Got None for %?", input)
		}
	}

	#[test]
	fn test_sexpr() {
		assert_eq!(parse(""), None);
		parse_some("123", Num(123.0));
		parse_some("()", List(~[]));
		parse_some("3.14159265358", Num(3.14159265358));
		parse_some("(hi)", List(~[Atom(~"hi")]));
		parse_some("(1)", List(~[Num(1.0)]));
		parse_some("(hi there)", List(~[Atom(~"hi"), Atom(~"there")]));
		parse_some("(hi (there))", List(~[Atom(~"hi"), List(~[Atom(~"there")])]));
		parse_some("(hi 123456)", List(~[Atom(~"hi"), Num(123456.0)]));
		parse_some("(())", List(~[List(~[])]));
		parse_some("(hi (there (fellow (human-bot!))))",
			List(~[Atom(~"hi"),
				List(~[Atom(~"there"),
					List(~[Atom(~"fellow"),
						List(~[Atom(~"human-bot!")])])])])
		);
	}

	#[test]
	fn test_whitespace() {
		// spaces
		parse_some("  (  1  )  ", List(~[Num(1.0)]));
		parse_some("  (  (  )  )  )", List(~[List(~[])]));

		// newlines
		parse_some("\n\n( \n\n )\n\n", List(~[]));
		parse_some("\n\n(\n\n123\n\n)\n\n", List(~[Num(123.0)]));
	}
}<|endoftext|>


<commit_before>/*
--- Day 5: How About a Nice Game of Chess? ---

You are faced with a security door designed by Easter Bunny engineers that seem to have acquired most of their security knowledge by watching hacking movies.

The eight-character password for the door is generated one character at a time by finding the MD5 hash of some Door ID (your puzzle input) and an increasing integer index (starting with 0).

A hash indicates the next character in the password if its hexadecimal representation starts with five zeroes. If it does, the sixth character in the hash is the next character of the password.

For example, if the Door ID is abc:

The first index which produces a hash that starts with five zeroes is 3231929, which we find by hashing abc3231929; the sixth character of the hash, and thus the first character of the password, is 1.
5017308 produces the next interesting hash, which starts with 000008f82..., so the second character of the password is 8.
The third time a hash starts with five zeroes is for abc5278568, discovering the character f.
In this example, after continuing this search a total of eight times, the password is 18f47a30.

Given the actual Door ID, what is the password?
*/

extern crate crypto;
use crypto::md5::Md5;
use crypto::digest::Digest;

fn main() {
    // make an empty string to store our password in
    let mut pass = String::new();

    // iterate forever until we break
    for i in 0..std::u64::MAX {
        // make a new MD5 hasher
        let mut hash = Md5::new();

        // concat our puzzle input with the index
        let input : &str = &format!("reyedfim{}", i);

        // the the concatenated string as input for our hasher
        hash.input_str(&input);

        // grab the result from the hash
        let result = hash.result_str();

        // grab the first 5 characters of the calculated hash
        let res_check = &result[..5];

        // if it starts with five zeroes, we have a match
        if res_check == "00000" {

            // what is the first character after those five zeroes?
            let next_char : char = result.chars().nth(5).unwrap();
            println!("found {:?}", next_char);

            // well thats part of our password. Lets store it 😜
            pass.push(next_char);

            // we don't quit until our calculated password is 8 character long
            if pass.len() == 8 {
                break;
            }

        }
    }

    // we got em. 😏
    println!("cracked the pass: {}", pass);
}
<commit_msg>Move puzzle input a bit<commit_after>/*
--- Day 5: How About a Nice Game of Chess? ---

You are faced with a security door designed by Easter Bunny engineers that seem to have acquired most of their security knowledge by watching hacking movies.

The eight-character password for the door is generated one character at a time by finding the MD5 hash of some Door ID (your puzzle input) and an increasing integer index (starting with 0).

A hash indicates the next character in the password if its hexadecimal representation starts with five zeroes. If it does, the sixth character in the hash is the next character of the password.

For example, if the Door ID is abc:

The first index which produces a hash that starts with five zeroes is 3231929, which we find by hashing abc3231929; the sixth character of the hash, and thus the first character of the password, is 1.
5017308 produces the next interesting hash, which starts with 000008f82..., so the second character of the password is 8.
The third time a hash starts with five zeroes is for abc5278568, discovering the character f.
In this example, after continuing this search a total of eight times, the password is 18f47a30.

Given the actual Door ID, what is the password?
*/

extern crate crypto;
use crypto::md5::Md5;
use crypto::digest::Digest;

fn main() {

    let puzzle_input = "reyedfim";

    // make an empty string to store our password in
    let mut pass = String::new();

    // iterate forever until we break
    for i in 0..std::u64::MAX {
        // make a new MD5 hasher
        let mut hash = Md5::new();

        // concat our puzzle input with the index
        let input : &str = &format!("{}{}", puzzle_input, i);

        // the the concatenated string as input for our hasher
        hash.input_str(&input);

        // grab the result from the hash
        let result = hash.result_str();

        // grab the first 5 characters of the calculated hash
        let res_check = &result[..5];

        // if it starts with five zeroes, we have a match
        if res_check == "00000" {

            // what is the first character after those five zeroes?
            let next_char : char = result.chars().nth(5).unwrap();
            println!("found {:?}", next_char);

            // well thats part of our password. Lets store it 😜
            pass.push(next_char);

            // we don't quit until our calculated password is 8 character long
            if pass.len() == 8 {
                break;
            }

        }
    }

    // we got em. 😏
    println!("cracked the pass: {}", pass);
}
<|endoftext|>


<commit_before>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::iter::FromIterator;
use std::str::FromStr;
use syn::{Lit, LitFloat, LitInt};

fn lit(s: &str) -> Lit {
    match TokenStream::from_str(s)
        .unwrap()
        .into_iter()
        .next()
        .unwrap()
    {
        TokenTree::Literal(lit) => Lit::new(lit),
        _ => panic!(),
    }
}

#[test]
fn strings() {
    fn test_string(s: &str, value: &str) {
        match lit(s) {
            Lit::Str(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_string("\"a\"", "a");
    test_string("\"\\n\"", "\n");
    test_string("\"\\r\"", "\r");
    test_string("\"\\t\"", "\t");
    test_string("\"🐕\"", "🐕"); // NOTE: This is an emoji
    test_string("\"\\\"\"", "\"");
    test_string("\"'\"", "'");
    test_string("\"\"", "");
    test_string("\"\\u{1F415}\"", "\u{1F415}");
    test_string(
        "\"contains\nnewlines\\\nescaped newlines\"",
        "contains\nnewlinesescaped newlines",
    );
    test_string("r\"raw\nstring\\\nhere\"", "raw\nstring\\\nhere");
    test_string("\"...\"q", "...");
    test_string("r\"...\"q", "...");
    test_string("r##\"...\"##q", "...");
}

#[test]
fn byte_strings() {
    fn test_byte_string(s: &str, value: &[u8]) {
        match lit(s) {
            Lit::ByteStr(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_byte_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte_string("b\"a\"", b"a");
    test_byte_string("b\"\\n\"", b"\n");
    test_byte_string("b\"\\r\"", b"\r");
    test_byte_string("b\"\\t\"", b"\t");
    test_byte_string("b\"\\\"\"", b"\"");
    test_byte_string("b\"'\"", b"'");
    test_byte_string("b\"\"", b"");
    test_byte_string(
        "b\"contains\nnewlines\\\nescaped newlines\"",
        b"contains\nnewlinesescaped newlines",
    );
    test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
    test_byte_string("b\"...\"q", b"...");
    test_byte_string("br\"...\"q", b"...");
    test_byte_string("br##\"...\"##q", b"...");
}

#[test]
fn bytes() {
    fn test_byte(s: &str, value: u8) {
        match lit(s) {
            Lit::Byte(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                assert_eq!(again, s);
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte("b'a'", b'a');
    test_byte("b'\\n'", b'\n');
    test_byte("b'\\r'", b'\r');
    test_byte("b'\\t'", b'\t');
    test_byte("b'\\''", b'\'');
    test_byte("b'\"'", b'"');
    test_byte("b'a'q", b'a');
}

#[test]
fn chars() {
    fn test_char(s: &str, value: char) {
        match lit(s) {
            Lit::Char(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_char(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_char("'a'", 'a');
    test_char("'\\n'", '\n');
    test_char("'\\r'", '\r');
    test_char("'\\t'", '\t');
    test_char("'🐕'", '🐕'); // NOTE: This is an emoji
    test_char("'\\''", '\'');
    test_char("'\"'", '"');
    test_char("'\\u{1F415}'", '\u{1F415}');
    test_char("'a'q", 'a');
}

#[test]
fn ints() {
    fn test_int(s: &str, value: u64, suffix: &str) {
        match lit(s) {
            Lit::Int(lit) => {
                assert_eq!(lit.base10_digits().parse::<u64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_int(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_int("5", 5, "");
    test_int("5u32", 5, "u32");
    test_int("0E", 0, "E");
    test_int("0ECMA", 0, "ECMA");
    test_int("0o0A", 0, "A");
    test_int("5_0", 50, "");
    test_int("5_____0_____", 50, "");
    test_int("0x7f", 127, "");
    test_int("0x7F", 127, "");
    test_int("0b1001", 9, "");
    test_int("0o73", 59, "");
    test_int("0x7Fu8", 127, "u8");
    test_int("0b1001i8", 9, "i8");
    test_int("0o73u32", 59, "u32");
    test_int("0x__7___f_", 127, "");
    test_int("0x__7___F_", 127, "");
    test_int("0b_1_0__01", 9, "");
    test_int("0o_7__3", 59, "");
    test_int("0x_7F__u8", 127, "u8");
    test_int("0b__10__0_1i8", 9, "i8");
    test_int("0o__7__________________3u32", 59, "u32");
}

#[test]
fn floats() {
    #[cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
    fn test_float(s: &str, value: f64, suffix: &str) {
        match lit(s) {
            Lit::Float(lit) => {
                assert_eq!(lit.base10_digits().parse::<f64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_float(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_float("5.5", 5.5, "");
    test_float("5.5E12", 5.5e12, "");
    test_float("5.5e12", 5.5e12, "");
    test_float("1.0__3e-12", 1.03e-12, "");
    test_float("1.03e+12", 1.03e12, "");
    test_float("9e99e99", 9e99, "e99");
    test_float("1e_0", 1.0, "");
}

#[test]
fn negative() {
    let span = Span::call_site();
    assert_eq!("-1", LitInt::new("-1", span).to_string());
    assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
    assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
    assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
    assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
    assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
    assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
    assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
}

#[test]
fn negative_overflow() {
    assert!(syn::parse_str::<LitFloat>("-1.0e99f64").is_ok());
    assert!(syn::parse_str::<LitFloat>("-1.0e999f64").is_err());
}

#[test]
fn suffix() {
    fn get_suffix(token: &str) -> String {
        let lit = syn::parse_str::<Lit>(token).unwrap();
        match lit {
            Lit::Str(lit) => lit.suffix().to_owned(),
            Lit::ByteStr(lit) => lit.suffix().to_owned(),
            Lit::Byte(lit) => lit.suffix().to_owned(),
            Lit::Char(lit) => lit.suffix().to_owned(),
            Lit::Int(lit) => lit.suffix().to_owned(),
            Lit::Float(lit) => lit.suffix().to_owned(),
            _ => unimplemented!(),
        }
    }

    assert_eq!(get_suffix("\"\"s"), "s");
    assert_eq!(get_suffix("r\"\"r"), "r");
    assert_eq!(get_suffix("b\"\"b"), "b");
    assert_eq!(get_suffix("br\"\"br"), "br");
    assert_eq!(get_suffix("r#\"\"#r"), "r");
    assert_eq!(get_suffix("'c'c"), "c");
    assert_eq!(get_suffix("b'b'b"), "b");
    assert_eq!(get_suffix("1i32"), "i32");
    assert_eq!(get_suffix("1_i32"), "i32");
    assert_eq!(get_suffix("1.0f32"), "f32");
    assert_eq!(get_suffix("1.0_f32"), "f32");
}

#[test]
fn test_deep_group_empty() {
    let tokens = TokenStream::from_iter(vec![TokenTree::Group(Group::new(
        Delimiter::None,
        TokenStream::from_iter(vec![TokenTree::Group(Group::new(
            Delimiter::None,
            TokenStream::from_iter(vec![TokenTree::Literal(Literal::string("hi"))]),
        ))]),
    ))]);

    snapshot!(tokens as Lit, @r#""hi""# );
}
<commit_msg>Add test of float literal with suffix starting with `E`<commit_after>#[macro_use]
mod macros;

use proc_macro2::{Delimiter, Group, Literal, Span, TokenStream, TokenTree};
use quote::ToTokens;
use std::iter::FromIterator;
use std::str::FromStr;
use syn::{Lit, LitFloat, LitInt};

fn lit(s: &str) -> Lit {
    match TokenStream::from_str(s)
        .unwrap()
        .into_iter()
        .next()
        .unwrap()
    {
        TokenTree::Literal(lit) => Lit::new(lit),
        _ => panic!(),
    }
}

#[test]
fn strings() {
    fn test_string(s: &str, value: &str) {
        match lit(s) {
            Lit::Str(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_string("\"a\"", "a");
    test_string("\"\\n\"", "\n");
    test_string("\"\\r\"", "\r");
    test_string("\"\\t\"", "\t");
    test_string("\"🐕\"", "🐕"); // NOTE: This is an emoji
    test_string("\"\\\"\"", "\"");
    test_string("\"'\"", "'");
    test_string("\"\"", "");
    test_string("\"\\u{1F415}\"", "\u{1F415}");
    test_string(
        "\"contains\nnewlines\\\nescaped newlines\"",
        "contains\nnewlinesescaped newlines",
    );
    test_string("r\"raw\nstring\\\nhere\"", "raw\nstring\\\nhere");
    test_string("\"...\"q", "...");
    test_string("r\"...\"q", "...");
    test_string("r##\"...\"##q", "...");
}

#[test]
fn byte_strings() {
    fn test_byte_string(s: &str, value: &[u8]) {
        match lit(s) {
            Lit::ByteStr(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_byte_string(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte_string("b\"a\"", b"a");
    test_byte_string("b\"\\n\"", b"\n");
    test_byte_string("b\"\\r\"", b"\r");
    test_byte_string("b\"\\t\"", b"\t");
    test_byte_string("b\"\\\"\"", b"\"");
    test_byte_string("b\"'\"", b"'");
    test_byte_string("b\"\"", b"");
    test_byte_string(
        "b\"contains\nnewlines\\\nescaped newlines\"",
        b"contains\nnewlinesescaped newlines",
    );
    test_byte_string("br\"raw\nstring\\\nhere\"", b"raw\nstring\\\nhere");
    test_byte_string("b\"...\"q", b"...");
    test_byte_string("br\"...\"q", b"...");
    test_byte_string("br##\"...\"##q", b"...");
}

#[test]
fn bytes() {
    fn test_byte(s: &str, value: u8) {
        match lit(s) {
            Lit::Byte(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                assert_eq!(again, s);
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_byte("b'a'", b'a');
    test_byte("b'\\n'", b'\n');
    test_byte("b'\\r'", b'\r');
    test_byte("b'\\t'", b'\t');
    test_byte("b'\\''", b'\'');
    test_byte("b'\"'", b'"');
    test_byte("b'a'q", b'a');
}

#[test]
fn chars() {
    fn test_char(s: &str, value: char) {
        match lit(s) {
            Lit::Char(lit) => {
                assert_eq!(lit.value(), value);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_char(&again, value);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_char("'a'", 'a');
    test_char("'\\n'", '\n');
    test_char("'\\r'", '\r');
    test_char("'\\t'", '\t');
    test_char("'🐕'", '🐕'); // NOTE: This is an emoji
    test_char("'\\''", '\'');
    test_char("'\"'", '"');
    test_char("'\\u{1F415}'", '\u{1F415}');
    test_char("'a'q", 'a');
}

#[test]
fn ints() {
    fn test_int(s: &str, value: u64, suffix: &str) {
        match lit(s) {
            Lit::Int(lit) => {
                assert_eq!(lit.base10_digits().parse::<u64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_int(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_int("5", 5, "");
    test_int("5u32", 5, "u32");
    test_int("0E", 0, "E");
    test_int("0ECMA", 0, "ECMA");
    test_int("0o0A", 0, "A");
    test_int("5_0", 50, "");
    test_int("5_____0_____", 50, "");
    test_int("0x7f", 127, "");
    test_int("0x7F", 127, "");
    test_int("0b1001", 9, "");
    test_int("0o73", 59, "");
    test_int("0x7Fu8", 127, "u8");
    test_int("0b1001i8", 9, "i8");
    test_int("0o73u32", 59, "u32");
    test_int("0x__7___f_", 127, "");
    test_int("0x__7___F_", 127, "");
    test_int("0b_1_0__01", 9, "");
    test_int("0o_7__3", 59, "");
    test_int("0x_7F__u8", 127, "u8");
    test_int("0b__10__0_1i8", 9, "i8");
    test_int("0o__7__________________3u32", 59, "u32");
}

#[test]
fn floats() {
    #[cfg_attr(feature = "cargo-clippy", allow(float_cmp))]
    fn test_float(s: &str, value: f64, suffix: &str) {
        match lit(s) {
            Lit::Float(lit) => {
                assert_eq!(lit.base10_digits().parse::<f64>().unwrap(), value);
                assert_eq!(lit.suffix(), suffix);
                let again = lit.into_token_stream().to_string();
                if again != s {
                    test_float(&again, value, suffix);
                }
            }
            wrong => panic!("{:?}", wrong),
        }
    }

    test_float("5.5", 5.5, "");
    test_float("5.5E12", 5.5e12, "");
    test_float("5.5e12", 5.5e12, "");
    test_float("1.0__3e-12", 1.03e-12, "");
    test_float("1.03e+12", 1.03e12, "");
    test_float("9e99e99", 9e99, "e99");
    test_float("1e_0", 1.0, "");
    test_float("0.0ECMA", 0.0, "ECMA");
}

#[test]
fn negative() {
    let span = Span::call_site();
    assert_eq!("-1", LitInt::new("-1", span).to_string());
    assert_eq!("-1i8", LitInt::new("-1i8", span).to_string());
    assert_eq!("-1i16", LitInt::new("-1i16", span).to_string());
    assert_eq!("-1i32", LitInt::new("-1i32", span).to_string());
    assert_eq!("-1i64", LitInt::new("-1i64", span).to_string());
    assert_eq!("-1.5", LitFloat::new("-1.5", span).to_string());
    assert_eq!("-1.5f32", LitFloat::new("-1.5f32", span).to_string());
    assert_eq!("-1.5f64", LitFloat::new("-1.5f64", span).to_string());
}

#[test]
fn negative_overflow() {
    assert!(syn::parse_str::<LitFloat>("-1.0e99f64").is_ok());
    assert!(syn::parse_str::<LitFloat>("-1.0e999f64").is_err());
}

#[test]
fn suffix() {
    fn get_suffix(token: &str) -> String {
        let lit = syn::parse_str::<Lit>(token).unwrap();
        match lit {
            Lit::Str(lit) => lit.suffix().to_owned(),
            Lit::ByteStr(lit) => lit.suffix().to_owned(),
            Lit::Byte(lit) => lit.suffix().to_owned(),
            Lit::Char(lit) => lit.suffix().to_owned(),
            Lit::Int(lit) => lit.suffix().to_owned(),
            Lit::Float(lit) => lit.suffix().to_owned(),
            _ => unimplemented!(),
        }
    }

    assert_eq!(get_suffix("\"\"s"), "s");
    assert_eq!(get_suffix("r\"\"r"), "r");
    assert_eq!(get_suffix("b\"\"b"), "b");
    assert_eq!(get_suffix("br\"\"br"), "br");
    assert_eq!(get_suffix("r#\"\"#r"), "r");
    assert_eq!(get_suffix("'c'c"), "c");
    assert_eq!(get_suffix("b'b'b"), "b");
    assert_eq!(get_suffix("1i32"), "i32");
    assert_eq!(get_suffix("1_i32"), "i32");
    assert_eq!(get_suffix("1.0f32"), "f32");
    assert_eq!(get_suffix("1.0_f32"), "f32");
}

#[test]
fn test_deep_group_empty() {
    let tokens = TokenStream::from_iter(vec![TokenTree::Group(Group::new(
        Delimiter::None,
        TokenStream::from_iter(vec![TokenTree::Group(Group::new(
            Delimiter::None,
            TokenStream::from_iter(vec![TokenTree::Literal(Literal::string("hi"))]),
        ))]),
    ))]);

    snapshot!(tokens as Lit, @r#""hi""# );
}
<|endoftext|>


<commit_before>extern mod extra;
use std::vec::from_elem;
use grid::{Row, Column, alive, dead, Cell, Grid};
mod grid;

/****************************************************************************
 * Something to say Ben??
 ****************************************************************************/

/*
 * Constructs an immutable grid from the contents of an array of strings.
 * The grid is declared mutable, and mutated only inside this fn.
*/
pub fn build_from_file_contents(file_contents: ~[~str]) -> Grid
{
  let height = file_contents.len();
  assert!(height > 0u);
  let width = file_contents[0].len();
  let cells = from_elem(width, Cell { value: alive } );

  let mut result = Grid {
      inner: from_elem(height, cells.clone())
  };
  for row in range(0, height) {
    for column in range(0, width) {
      let file_value: char = file_contents[row][column] as char;
      let cell_value = match file_value {
        'o' => alive,
        '.' => dead,
         _  => fail!("Unexpected cell value found in file.")
      };
      result.inner[row][column] = Cell { value: cell_value };
   }
  }
  return result;
} // fn build_from_file_contents

enum direction {
  above, below, left, right
}  // enum CellValue

/*
  Calculates the neighbor's position given the inputs.
*/
fn neighbor_pos(Row(row): Row, Column(column): Column, dir: direction)
    -> (Row, Column)
{
  return match dir {
    above => (Row(row - 1), Column(column)),
    below => (Row(row + 1), Column(column)),
    right => (Row(row), Column(column + 1)),
    left  => (Row(row), Column(column - 1))
  };
}  // fn neighbor_pos

/*
 * Returns a count for how many neighbors of a cell in the grid
 * are alive.
*/
pub fn count_neighbors(row: Row, column: Column, grid: &Grid)
    -> uint
{
  let (same_row, left_column)  = neighbor_pos(row, column, left);
  let (same_row, right_column) = neighbor_pos(row, column, right);
  let (above_row, same_column) = neighbor_pos(row, column, above);
  let (below_row, same_column) = neighbor_pos(row, column, below);

  let (above_left_row, above_left_column) = neighbor_pos(above_row, same_column, left);
  let (above_right_row, above_right_column) = neighbor_pos(above_row, same_column, right);
  let (below_left_row, below_left_column) = neighbor_pos(below_row, same_column, left);
  let (below_right_row, below_right_column) = neighbor_pos(below_row, same_column, right);

  let c0 = grid.cell_alive(same_row, left_column);
  let c1 = grid.cell_alive(same_row, right_column);
  let c2 = grid.cell_alive(above_row, same_column);
  let c3 = grid.cell_alive(below_row, same_column);

  let c4 = grid.cell_alive(above_left_row, above_left_column);
  let c5 = grid.cell_alive(above_right_row, above_right_column);
  let c6 = grid.cell_alive(below_left_row, below_left_column);
  let c7 = grid.cell_alive(below_right_row, below_right_column);
  return c0 + c1 + c2 + c3 + c4 + c5 + c6 + c7;
}

/*pub fn print_neighbor_count(grid: &Grid) {
  let x = grid.inner.len();
  for row in range(0, x) {
    for column in range(0, x) {
      let r = Row(row);
      let c = Column(column);
      let n = count_neighbors(r, c, grid);
      print!("{:u}", n);
    }
    println!("");
  }
}  // fn print_neighbor_count
*/

// 1)Any live cell with fewer than two live neighbours dies, as if caused by
// under-population.
// 2) Any live cell with two or three live neighbours lives on to the next
// generation.
// 3) Any live cell with more than three live neighbours dies, as if by
// overcrowding.
// 4) Any dead cell with exactly three live neighbours becomes a live cell, as
// if by reproduction.
pub fn build_from_grid(prevg: &Grid) -> Grid
{
  let cell_value = |row: uint, column: uint| {
    let ncount = count_neighbors(Row(row), Column(column), prevg);
    let cv = match (prevg.inner[row][column].value, ncount) {
      (dead, 3)       => alive,
      (alive, 2..3)   => alive,
      _               => dead
    };
    return Cell { value: cv };
  };
  let mut result = prevg.clone();
  for row in range(0, prevg.height()) {
    for column in range(0, prevg.width()) {
      result.inner[row][column] = cell_value(row, column);
    }
  }
  return result;
}  // fn build_from_grid
<commit_msg>Refactored count_neighbors!<commit_after>extern mod extra;
use std::vec::from_elem;
use grid::{Row, Column, alive, dead, Cell, Grid};
mod grid;

/****************************************************************************
 * Something to say Ben??
 ****************************************************************************/

/*
 * Constructs an immutable grid from the contents of an array of strings.
 * The grid is declared mutable, and mutated only inside this fn.
*/
pub fn build_from_file_contents(file_contents: ~[~str]) -> Grid
{
  let height = file_contents.len();
  assert!(height > 0u);
  let width = file_contents[0].len();
  let cells = from_elem(width, Cell { value: alive } );

  let mut result = Grid {
      inner: from_elem(height, cells.clone())
  };
  for row in range(0, height) {
    for column in range(0, width) {
      let file_value: char = file_contents[row][column] as char;
      let cell_value = match file_value {
        'o' => alive,
        '.' => dead,
         _  => fail!("Unexpected cell value found in file.")
      };
      result.inner[row][column] = Cell { value: cell_value };
   }
  }
  return result;
} // fn build_from_file_contents

/*
 Returns a count for how many neighbors of a cell in the grid
 are alive.
 Starts to the cell to the left of the cell/row and sums up cell_alive
 working in clockwise order.
*/
fn count_neighbors(Row(row): Row, Column(col): Column, grid: &Grid) -> uint {
  let left_column  = Column(col - 1);
  let right_column = Column(col + 1);
  let above_row    = Row(row - 1);
  let below_row    = Row(row + 1);

  return grid.cell_alive(Row(row), left_column) + // left
      grid.cell_alive(above_row, left_column)   + // left-above
      grid.cell_alive(above_row, Column(col))   + // above
      grid.cell_alive(above_row, right_column)  + // above-right
      grid.cell_alive(Row(row), right_column)   + // right
      grid.cell_alive(below_row, right_column)  + // below-right
      grid.cell_alive(below_row, Column(col))   + // below
      grid.cell_alive(below_row, left_column);    // below-left
}  // fn count_neighbors

/*pub fn print_neighbor_count(grid: &Grid) {
  let x = grid.inner.len();
  for row in range(0, x) {
    for column in range(0, x) {
      let r = Row(row);
      let c = Column(column);
      let n = count_neighbors(r, c, grid);
      print!("{:u}", n);
    }
    println!("");
  }
}  // fn print_neighbor_count
*/

// 1)Any live cell with fewer than two live neighbours dies, as if caused by
// under-population.
// 2) Any live cell with two or three live neighbours lives on to the next
// generation.
// 3) Any live cell with more than three live neighbours dies, as if by
// overcrowding.
// 4) Any dead cell with exactly three live neighbours becomes a live cell, as
// if by reproduction.
pub fn build_from_grid(prevg: &Grid) -> Grid
{
  let cell_value = |row: uint, column: uint| {
    let ncount = count_neighbors(Row(row), Column(column), prevg);
    let cv = match (prevg.inner[row][column].value, ncount) {
      (dead, 3)       => alive,
      (alive, 2..3)   => alive,
      _               => dead
    };
    return Cell { value: cv };
  };
  let mut result = prevg.clone();
  for row in range(0, prevg.height()) {
    for column in range(0, prevg.width()) {
      result.inner[row][column] = cell_value(row, column);
    }
  }
  return result;
}  // fn build_from_grid
<|endoftext|>


<commit_before>#[macro_use]
extern crate log;
#[macro_use]
extern crate futures;
extern crate bytes;
extern crate futures_cpupool;
extern crate tokio_core;
extern crate tls_api;
extern crate tls_api_stub;
extern crate tokio_tls_api;
extern crate base64;

// renamed to avoid name conflict with local protobuf library
extern crate protobuf as protobuf_lib;

extern crate httpbis;

mod futures_misc;
mod misc;

mod client;
mod server;
mod server_method;

mod assert_types;

mod grpc;
mod grpc_frame;
mod grpc_http_to_response;
mod result;
mod stream_item;
mod req;
mod resp;
mod chars;

mod method;
mod marshall;
mod futures_grpc;
mod error;
mod iter;
mod metadata;

pub mod rt;
pub mod protobuf;

pub mod for_test;

pub use error::Error;
pub use error::GrpcMessageError;
pub use result::Result;

pub use stream_item::ItemOrMetadata;

pub use client::Client;
pub use client::ClientConf;

pub use server::Server;
pub use server::ServerBuilder;
pub use server::ServerConf;

pub use resp::SingleResponse;
pub use resp::StreamingResponse;

pub use req::RequestOptions;
pub use req::StreamingRequest;

pub use futures_grpc::GrpcStream;
pub use futures_grpc::GrpcFuture;

pub use metadata::Metadata;
pub use metadata::MetadataKey;
<commit_msg>Make GrpcStatus public. Partially addresses issue #102.<commit_after>#[macro_use]
extern crate log;
#[macro_use]
extern crate futures;
extern crate bytes;
extern crate futures_cpupool;
extern crate tokio_core;
extern crate tls_api;
extern crate tls_api_stub;
extern crate tokio_tls_api;
extern crate base64;

// renamed to avoid name conflict with local protobuf library
extern crate protobuf as protobuf_lib;

extern crate httpbis;

mod futures_misc;
mod misc;

mod client;
mod server;
mod server_method;

mod assert_types;

mod grpc;
mod grpc_frame;
mod grpc_http_to_response;
mod result;
mod stream_item;
mod req;
mod resp;
mod chars;

mod method;
mod marshall;
mod futures_grpc;
mod error;
mod iter;
mod metadata;

pub mod rt;
pub mod protobuf;

pub mod for_test;

pub use error::Error;
pub use error::GrpcMessageError;
pub use grpc::GrpcStatus
pub use result::Result;

pub use stream_item::ItemOrMetadata;

pub use client::Client;
pub use client::ClientConf;

pub use server::Server;
pub use server::ServerBuilder;
pub use server::ServerConf;

pub use resp::SingleResponse;
pub use resp::StreamingResponse;

pub use req::RequestOptions;
pub use req::StreamingRequest;

pub use futures_grpc::GrpcStream;
pub use futures_grpc::GrpcFuture;

pub use metadata::Metadata;
pub use metadata::MetadataKey;
<|endoftext|>


<commit_before>use crate::plot_data::PlotData;
use anyhow::{anyhow, Result};
use chrono::prelude::*;
use const_format::formatcp;
use crossterm::event::{KeyEvent, KeyModifiers};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dns_lookup::lookup_host;
use pinger::{ping_with_interval, PingResult};
use std::io;
use std::iter;
use std::net::IpAddr;
use std::ops::Add;
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::sync::{mpsc, Arc};
use std::thread;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset};
use tui::Terminal;
mod plot_data;

use shadow_rs::shadow;

shadow!(build);

const VERSION_INFO: &str = formatcp!(
    r#"{}
commit_hash: {}
build_time: {}
build_env: {},{}"#,
    build::PKG_VERSION,
    build::SHORT_COMMIT,
    build::BUILD_TIME,
    build::RUST_VERSION,
    build::RUST_CHANNEL
);

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.", version=VERSION_INFO)]
struct Args {
    #[structopt(
        long,
        help = "Graph the execution time for a list of commands rather than pinging hosts"
    )]
    cmd: bool,
    #[structopt(
        short = "n",
        long,
        help = "Watch interval seconds (provide partial seconds like '0.5'). Default for ping is 0.2, default for cmd is 0.5."
    )]
    watch_interval: Option<f32>,
    #[structopt(
        help = "Hosts or IPs to ping, or commands to run if --cmd is provided.",
        required = true
    )]
    hosts_or_commands: Vec<String>,
    #[structopt(
        short,
        long,
        default_value = "30",
        help = "Determines the number of seconds to display in the graph."
    )]
    buffer: u64,
    /// Resolve ping targets to IPv4 address
    #[structopt(short = "4", conflicts_with = "ipv6")]
    ipv4: bool,
    /// Resolve ping targets to IPv6 address
    #[structopt(short = "6", conflicts_with = "ipv4")]
    ipv6: bool,
    #[structopt(short = "s", long, help = "Uses dot characters instead of braille")]
    simple_graphics: bool,
}

struct App {
    data: Vec<PlotData>,
    display_interval: chrono::Duration,
    started: chrono::DateTime<Local>,
}

impl App {
    fn new(data: Vec<PlotData>, buffer: u64) -> Self {
        App {
            data,
            display_interval: chrono::Duration::from_std(Duration::from_secs(buffer)).unwrap(),
            started: Local::now(),
        }
    }

    fn update(&mut self, host_idx: usize, item: Option<Duration>) {
        let host = &mut self.data[host_idx];
        host.update(item);
    }

    fn y_axis_bounds(&self) -> [f64; 2] {
        // Find the Y axis bounds for our chart.
        // This is trickier than the x-axis. We iterate through all our PlotData structs
        // and find the min/max of all the values. Then we add a 10% buffer to them.
        let iter = self
            .data
            .iter()
            .map(|b| b.data.as_slice())
            .flatten()
            .map(|v| v.1);
        let min = iter.clone().fold(f64::INFINITY, |a, b| a.min(b));
        let max = iter.fold(0f64, |a, b| a.max(b));
        // Add a 10% buffer to the top and bottom
        let max_10_percent = (max * 10_f64) / 100_f64;
        let min_10_percent = (min * 10_f64) / 100_f64;
        [min - min_10_percent, max + max_10_percent]
    }

    fn x_axis_bounds(&self) -> [f64; 2] {
        let now = Local::now();
        let now_idx;
        let before_idx;
        if (now - self.started) < self.display_interval {
            now_idx = (self.started + self.display_interval).timestamp_millis() as f64 / 1_000f64;
            before_idx = self.started.timestamp_millis() as f64 / 1_000f64;
        } else {
            now_idx = now.timestamp_millis() as f64 / 1_000f64;
            let before = now - self.display_interval;
            before_idx = before.timestamp_millis() as f64 / 1_000f64;
        }

        [before_idx, now_idx]
    }

    fn x_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        let lower_utc = NaiveDateTime::from_timestamp(bounds[0] as i64, 0);
        let upper_utc = NaiveDateTime::from_timestamp(bounds[1] as i64, 0);
        let lower = Local::from_utc_datetime(&Local, &lower_utc);
        let upper = Local::from_utc_datetime(&Local, &upper_utc);
        let diff = (upper - lower) / 2;
        let midpoint = lower + diff;
        return vec![
            Span::raw(format!("{:?}", lower.time())),
            Span::raw(format!("{:?}", midpoint.time())),
            Span::raw(format!("{:?}", upper.time())),
        ];
    }

    fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Create 7 labels for our y axis, based on the y-axis bounds we computed above.
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let num_labels = 7;
        // Split difference into one chunk for each of the 7 labels
        let increment = Duration::from_micros((difference / num_labels as f64) as u64);
        let duration = Duration::from_micros(min as u64);

        (0..num_labels)
            .map(|i| Span::raw(format!("{:?}", duration.add(increment * i))))
            .collect()
    }
}

#[derive(Debug)]
enum Update {
    Result(Duration),
    Timeout,
    Unknown,
}

impl From<PingResult> for Update {
    fn from(result: PingResult) -> Self {
        match result {
            PingResult::Pong(duration, _) => Update::Result(duration),
            PingResult::Timeout(_) => Update::Timeout,
            PingResult::Unknown(_) => Update::Unknown,
        }
    }
}

#[derive(Debug)]
enum Event {
    Update(usize, Update),
    Input(KeyEvent),
}

fn start_cmd_thread(
    watch_cmd: &str,
    host_id: usize,
    watch_interval: Option<f32>,
    cmd_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let mut words = watch_cmd.split_ascii_whitespace();
    let cmd = words
        .next()
        .expect("Must specify a command to watch")
        .to_string();
    let cmd_args = words
        .into_iter()
        .map(|w| w.to_string())
        .collect::<Vec<String>>();

    let interval = Duration::from_millis((watch_interval.unwrap_or(0.5) * 1000.0) as u64);

    // Pump cmd watches into the queue
    thread::spawn(move || -> Result<()> {
        while !kill_event.load(Ordering::Acquire) {
            let start = Instant::now();
            let mut child = Command::new(&cmd)
                .args(&cmd_args)
                .stderr(Stdio::null())
                .stdout(Stdio::null())
                .spawn()?;
            let status = child.wait()?;
            let duration = start.elapsed();
            let update = if status.success() {
                Update::Result(duration)
            } else {
                Update::Timeout
            };
            cmd_tx.send(Event::Update(host_id, update))?;
            thread::sleep(interval);
        }
        Ok(())
    })
}

fn start_ping_thread(
    host: String,
    host_id: usize,
    watch_interval: Option<f32>,
    ping_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let interval = Duration::from_millis((watch_interval.unwrap_or(0.2) * 1000.0) as u64);
    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping_with_interval(host, interval)?;
        while !kill_event.load(Ordering::Acquire) {
            ping_tx.send(Event::Update(host_id, stream.recv()?.into()))?;
        }
        Ok(())
    })
}

fn get_host_ipaddr(host: &str, force_ipv4: bool, force_ipv6: bool) -> Result<String> {
    let ipaddr: Vec<IpAddr> = match lookup_host(host) {
        Ok(ip) => ip,
        Err(_) => return Err(anyhow!("Could not resolve hostname {}", host)),
    };
    let ipaddr = if force_ipv4 {
        ipaddr
            .iter()
            .find(|ip| matches!(ip, IpAddr::V4(_)))
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv4", host))
    } else if force_ipv6 {
        ipaddr
            .iter()
            .find(|ip| matches!(ip, IpAddr::V6(_)))
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv6", host))
    } else {
        ipaddr
            .first()
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IP", host))
    };
    Ok(ipaddr?.to_string())
}

fn main() -> Result<()> {
    let args = Args::from_args();

    let mut data = vec![];

    for (idx, host_or_cmd) in args.hosts_or_commands.iter().enumerate() {
        let display = match args.cmd {
            true => host_or_cmd.to_string(),
            false => format!(
                "{} ({})",
                host_or_cmd,
                get_host_ipaddr(host_or_cmd, args.ipv4, args.ipv6)?
            ),
        };
        data.push(PlotData::new(
            display,
            args.buffer,
            Style::default().fg(Color::Indexed(idx as u8 + 2)),
            args.simple_graphics,
        ));
    }

    let mut app = App::new(data, args.buffer);
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;

    let (key_tx, rx) = mpsc::channel();

    let mut threads = vec![];

    let killed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));

    for (host_id, host_or_cmd) in args.hosts_or_commands.iter().cloned().enumerate() {
        if args.cmd {
            let cmd_thread = start_cmd_thread(
                &host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            );
            threads.push(cmd_thread);
        } else {
            threads.push(start_ping_thread(
                host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            ));
        }
    }

    // Pump keyboard messages into the queue
    let killed_thread = std::sync::Arc::clone(&killed);
    let key_thread = thread::spawn(move || -> Result<()> {
        while !killed_thread.load(Ordering::Acquire) {
            if event::poll(Duration::from_millis(100))? {
                if let CEvent::Key(key) = event::read()? {
                    key_tx.send(Event::Input(key))?;
                }
            }
        }
        Ok(())
    });
    threads.push(key_thread);

    loop {
        match rx.recv()? {
            Event::Update(host_id, update) => {
                match update {
                    Update::Result(duration) => app.update(host_id, Some(duration)),
                    Update::Timeout => app.update(host_id, None),
                    Update::Unknown => (),
                };
                terminal.draw(|f| {
                    // Split our
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(1)
                        .constraints(
                            iter::repeat(Constraint::Length(1))
                                .take(app.data.len())
                                .chain(iter::once(Constraint::Percentage(10)))
                                .collect::<Vec<_>>()
                                .as_ref(),
                        )
                        .split(f.size());

                    let total_chunks = chunks.len();

                    let header_chunks = chunks[0..total_chunks - 1].to_owned();
                    let chart_chunk = chunks[total_chunks - 1].to_owned();

                    for (plot_data, chunk) in app.data.iter().zip(header_chunks) {
                        let header_layout = Layout::default()
                            .direction(Direction::Horizontal)
                            .constraints(
                                [
                                    Constraint::Percentage(28),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                ]
                                .as_ref(),
                            )
                            .split(chunk);

                        for (area, paragraph) in
                            header_layout.into_iter().zip(plot_data.header_stats())
                        {
                            f.render_widget(paragraph, area);
                        }
                    }

                    let datasets: Vec<Dataset> = app.data.iter().map(|d| d.into()).collect();

                    let y_axis_bounds = app.y_axis_bounds();
                    let x_axis_bounds = app.x_axis_bounds();

                    let chart = Chart::new(datasets)
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(x_axis_bounds)
                                .labels(app.x_axis_labels(x_axis_bounds)),
                        )
                        .y_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );

                    f.render_widget(chart, chart_chunk)
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                _ => {}
            },
        }
    }

    for thread in threads {
        thread.join().unwrap()?;
    }

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}
<commit_msg>Add customisable vertical and horizontal margins (#155)<commit_after>use crate::plot_data::PlotData;
use anyhow::{anyhow, Result};
use chrono::prelude::*;
use const_format::formatcp;
use crossterm::event::{KeyEvent, KeyModifiers};
use crossterm::{
    event::{self, DisableMouseCapture, EnableMouseCapture, Event as CEvent, KeyCode},
    execute,
    terminal::{disable_raw_mode, enable_raw_mode, EnterAlternateScreen, LeaveAlternateScreen},
};
use dns_lookup::lookup_host;
use pinger::{ping_with_interval, PingResult};
use std::io;
use std::iter;
use std::net::IpAddr;
use std::ops::Add;
use std::process::{Command, Stdio};
use std::sync::atomic::{AtomicBool, Ordering};
use std::sync::mpsc::Sender;
use std::sync::{mpsc, Arc};
use std::thread;
use std::thread::JoinHandle;
use std::time::{Duration, Instant};
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset};
use tui::Terminal;
mod plot_data;

use shadow_rs::shadow;

shadow!(build);

const VERSION_INFO: &str = formatcp!(
    r#"{}
commit_hash: {}
build_time: {}
build_env: {},{}"#,
    build::PKG_VERSION,
    build::SHORT_COMMIT,
    build::BUILD_TIME,
    build::RUST_VERSION,
    build::RUST_CHANNEL
);

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.", version=VERSION_INFO)]
struct Args {
    #[structopt(
        long,
        help = "Graph the execution time for a list of commands rather than pinging hosts"
    )]
    cmd: bool,
    #[structopt(
        short = "n",
        long,
        help = "Watch interval seconds (provide partial seconds like '0.5'). Default for ping is 0.2, default for cmd is 0.5."
    )]
    watch_interval: Option<f32>,
    #[structopt(
        help = "Hosts or IPs to ping, or commands to run if --cmd is provided.",
        required = true
    )]
    hosts_or_commands: Vec<String>,
    #[structopt(
        short,
        long,
        default_value = "30",
        help = "Determines the number of seconds to display in the graph."
    )]
    buffer: u64,
    /// Resolve ping targets to IPv4 address
    #[structopt(short = "4", conflicts_with = "ipv6")]
    ipv4: bool,
    /// Resolve ping targets to IPv6 address
    #[structopt(short = "6", conflicts_with = "ipv4")]
    ipv6: bool,
    #[structopt(short = "s", long, help = "Uses dot characters instead of braille")]
    simple_graphics: bool,
    #[structopt(
        long,
        help = "Vertical margin around the graph (top and bottom)",
        default_value = "1"
    )]
    vertical_margin: u16,
    #[structopt(
        long,
        help = "Horizontal margin around the graph (left and right)",
        default_value = "0"
    )]
    horizontal_margin: u16,
}

struct App {
    data: Vec<PlotData>,
    display_interval: chrono::Duration,
    started: chrono::DateTime<Local>,
}

impl App {
    fn new(data: Vec<PlotData>, buffer: u64) -> Self {
        App {
            data,
            display_interval: chrono::Duration::from_std(Duration::from_secs(buffer)).unwrap(),
            started: Local::now(),
        }
    }

    fn update(&mut self, host_idx: usize, item: Option<Duration>) {
        let host = &mut self.data[host_idx];
        host.update(item);
    }

    fn y_axis_bounds(&self) -> [f64; 2] {
        // Find the Y axis bounds for our chart.
        // This is trickier than the x-axis. We iterate through all our PlotData structs
        // and find the min/max of all the values. Then we add a 10% buffer to them.
        let iter = self
            .data
            .iter()
            .map(|b| b.data.as_slice())
            .flatten()
            .map(|v| v.1);
        let min = iter.clone().fold(f64::INFINITY, |a, b| a.min(b));
        let max = iter.fold(0f64, |a, b| a.max(b));
        // Add a 10% buffer to the top and bottom
        let max_10_percent = (max * 10_f64) / 100_f64;
        let min_10_percent = (min * 10_f64) / 100_f64;
        [min - min_10_percent, max + max_10_percent]
    }

    fn x_axis_bounds(&self) -> [f64; 2] {
        let now = Local::now();
        let now_idx;
        let before_idx;
        if (now - self.started) < self.display_interval {
            now_idx = (self.started + self.display_interval).timestamp_millis() as f64 / 1_000f64;
            before_idx = self.started.timestamp_millis() as f64 / 1_000f64;
        } else {
            now_idx = now.timestamp_millis() as f64 / 1_000f64;
            let before = now - self.display_interval;
            before_idx = before.timestamp_millis() as f64 / 1_000f64;
        }

        [before_idx, now_idx]
    }

    fn x_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        let lower_utc = NaiveDateTime::from_timestamp(bounds[0] as i64, 0);
        let upper_utc = NaiveDateTime::from_timestamp(bounds[1] as i64, 0);
        let lower = Local::from_utc_datetime(&Local, &lower_utc);
        let upper = Local::from_utc_datetime(&Local, &upper_utc);
        let diff = (upper - lower) / 2;
        let midpoint = lower + diff;
        return vec![
            Span::raw(format!("{:?}", lower.time())),
            Span::raw(format!("{:?}", midpoint.time())),
            Span::raw(format!("{:?}", upper.time())),
        ];
    }

    fn y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Create 7 labels for our y axis, based on the y-axis bounds we computed above.
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let num_labels = 7;
        // Split difference into one chunk for each of the 7 labels
        let increment = Duration::from_micros((difference / num_labels as f64) as u64);
        let duration = Duration::from_micros(min as u64);

        (0..num_labels)
            .map(|i| Span::raw(format!("{:?}", duration.add(increment * i))))
            .collect()
    }
}

#[derive(Debug)]
enum Update {
    Result(Duration),
    Timeout,
    Unknown,
}

impl From<PingResult> for Update {
    fn from(result: PingResult) -> Self {
        match result {
            PingResult::Pong(duration, _) => Update::Result(duration),
            PingResult::Timeout(_) => Update::Timeout,
            PingResult::Unknown(_) => Update::Unknown,
        }
    }
}

#[derive(Debug)]
enum Event {
    Update(usize, Update),
    Input(KeyEvent),
}

fn start_cmd_thread(
    watch_cmd: &str,
    host_id: usize,
    watch_interval: Option<f32>,
    cmd_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let mut words = watch_cmd.split_ascii_whitespace();
    let cmd = words
        .next()
        .expect("Must specify a command to watch")
        .to_string();
    let cmd_args = words
        .into_iter()
        .map(|w| w.to_string())
        .collect::<Vec<String>>();

    let interval = Duration::from_millis((watch_interval.unwrap_or(0.5) * 1000.0) as u64);

    // Pump cmd watches into the queue
    thread::spawn(move || -> Result<()> {
        while !kill_event.load(Ordering::Acquire) {
            let start = Instant::now();
            let mut child = Command::new(&cmd)
                .args(&cmd_args)
                .stderr(Stdio::null())
                .stdout(Stdio::null())
                .spawn()?;
            let status = child.wait()?;
            let duration = start.elapsed();
            let update = if status.success() {
                Update::Result(duration)
            } else {
                Update::Timeout
            };
            cmd_tx.send(Event::Update(host_id, update))?;
            thread::sleep(interval);
        }
        Ok(())
    })
}

fn start_ping_thread(
    host: String,
    host_id: usize,
    watch_interval: Option<f32>,
    ping_tx: Sender<Event>,
    kill_event: Arc<AtomicBool>,
) -> JoinHandle<Result<()>> {
    let interval = Duration::from_millis((watch_interval.unwrap_or(0.2) * 1000.0) as u64);
    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping_with_interval(host, interval)?;
        while !kill_event.load(Ordering::Acquire) {
            ping_tx.send(Event::Update(host_id, stream.recv()?.into()))?;
        }
        Ok(())
    })
}

fn get_host_ipaddr(host: &str, force_ipv4: bool, force_ipv6: bool) -> Result<String> {
    let ipaddr: Vec<IpAddr> = match lookup_host(host) {
        Ok(ip) => ip,
        Err(_) => return Err(anyhow!("Could not resolve hostname {}", host)),
    };
    let ipaddr = if force_ipv4 {
        ipaddr
            .iter()
            .find(|ip| matches!(ip, IpAddr::V4(_)))
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv4", host))
    } else if force_ipv6 {
        ipaddr
            .iter()
            .find(|ip| matches!(ip, IpAddr::V6(_)))
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IPv6", host))
    } else {
        ipaddr
            .first()
            .ok_or_else(|| anyhow!("Could not resolve '{}' to IP", host))
    };
    Ok(ipaddr?.to_string())
}

fn main() -> Result<()> {
    let args = Args::from_args();

    let mut data = vec![];

    for (idx, host_or_cmd) in args.hosts_or_commands.iter().enumerate() {
        let display = match args.cmd {
            true => host_or_cmd.to_string(),
            false => format!(
                "{} ({})",
                host_or_cmd,
                get_host_ipaddr(host_or_cmd, args.ipv4, args.ipv6)?
            ),
        };
        data.push(PlotData::new(
            display,
            args.buffer,
            Style::default().fg(Color::Indexed(idx as u8 + 2)),
            args.simple_graphics,
        ));
    }

    let mut app = App::new(data, args.buffer);
    enable_raw_mode()?;
    let mut stdout = io::stdout();
    execute!(stdout, EnterAlternateScreen, EnableMouseCapture)?;
    let backend = CrosstermBackend::new(stdout);

    let mut terminal = Terminal::new(backend)?;

    terminal.clear()?;

    let (key_tx, rx) = mpsc::channel();

    let mut threads = vec![];

    let killed = std::sync::Arc::new(std::sync::atomic::AtomicBool::new(false));

    for (host_id, host_or_cmd) in args.hosts_or_commands.iter().cloned().enumerate() {
        if args.cmd {
            let cmd_thread = start_cmd_thread(
                &host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            );
            threads.push(cmd_thread);
        } else {
            threads.push(start_ping_thread(
                host_or_cmd,
                host_id,
                args.watch_interval,
                key_tx.clone(),
                std::sync::Arc::clone(&killed),
            ));
        }
    }

    // Pump keyboard messages into the queue
    let killed_thread = std::sync::Arc::clone(&killed);
    let key_thread = thread::spawn(move || -> Result<()> {
        while !killed_thread.load(Ordering::Acquire) {
            if event::poll(Duration::from_millis(100))? {
                if let CEvent::Key(key) = event::read()? {
                    key_tx.send(Event::Input(key))?;
                }
            }
        }
        Ok(())
    });
    threads.push(key_thread);

    loop {
        match rx.recv()? {
            Event::Update(host_id, update) => {
                match update {
                    Update::Result(duration) => app.update(host_id, Some(duration)),
                    Update::Timeout => app.update(host_id, None),
                    Update::Unknown => (),
                };
                terminal.draw(|f| {
                    // Split our
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .vertical_margin(args.vertical_margin)
                        .horizontal_margin(args.horizontal_margin)
                        .constraints(
                            iter::repeat(Constraint::Length(1))
                                .take(app.data.len())
                                .chain(iter::once(Constraint::Percentage(10)))
                                .collect::<Vec<_>>()
                                .as_ref(),
                        )
                        .split(f.size());

                    let total_chunks = chunks.len();

                    let header_chunks = chunks[0..total_chunks - 1].to_owned();
                    let chart_chunk = chunks[total_chunks - 1].to_owned();

                    for (plot_data, chunk) in app.data.iter().zip(header_chunks) {
                        let header_layout = Layout::default()
                            .direction(Direction::Horizontal)
                            .constraints(
                                [
                                    Constraint::Percentage(28),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                    Constraint::Percentage(12),
                                ]
                                .as_ref(),
                            )
                            .split(chunk);

                        for (area, paragraph) in
                            header_layout.into_iter().zip(plot_data.header_stats())
                        {
                            f.render_widget(paragraph, area);
                        }
                    }

                    let datasets: Vec<Dataset> = app.data.iter().map(|d| d.into()).collect();

                    let y_axis_bounds = app.y_axis_bounds();
                    let x_axis_bounds = app.x_axis_bounds();

                    let chart = Chart::new(datasets)
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(x_axis_bounds)
                                .labels(app.x_axis_labels(x_axis_bounds)),
                        )
                        .y_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );

                    f.render_widget(chart, chart_chunk)
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    killed.store(true, Ordering::Release);
                    break;
                }
                _ => {}
            },
        }
    }

    for thread in threads {
        thread.join().unwrap()?;
    }

    disable_raw_mode()?;
    execute!(
        terminal.backend_mut(),
        LeaveAlternateScreen,
        DisableMouseCapture
    )?;
    terminal.show_cursor()?;

    Ok(())
}
<|endoftext|>