<commit_before>extern crate orbimage;

use self::orbimage::Image;

use super::{CloneCell, Color, Event, Placeable, Point, Rect, Renderer, Widget, WidgetCore};
use super::callback::Click;
use super::cell::CheckSet;

use std::cell::Cell;
use std::sync::Arc;

pub struct Menu {
    pub core: WidgetCore,
    text: CloneCell<String>,
    bg_pressed: Color,
    fg_border: Color,
    text_offset: Point,
    entries: Vec<Box<Entry>>,
    click_callback: Option<Arc<Fn(&Menu, Point)>>,
    pressed: Cell<bool>,
    activated: Cell<bool>,
}

pub struct Action {
    core: WidgetCore,
    text: CloneCell<String>,
    icon: Option<Image>,
    bg_pressed: Color,
    text_offset: Point,
    click_callback: Option<Arc<Fn(&Action, Point)>>,
    pressed: Cell<bool>,
    hover: Cell<bool>,
}

pub struct Separator {
    core: WidgetCore,
}

pub trait Entry: Widget {
    fn text(&mut self) -> String;
}

impl Menu {
    pub fn new<S: Into<String>>(name: S) -> Self {
        Menu {
            core: WidgetCore::new()
                    .bg(Color::rgb(234, 234, 234)),
            text: CloneCell::new(name.into()),
            bg_pressed: Color::rgb(210, 210, 208),
            fg_border: Color::rgb(209, 209, 208),
            text_offset: Point::default(),
            entries: Vec::with_capacity(10),
            click_callback: None,
            pressed: Cell::new(false),
            activated: Cell::new(false),
        }
    }

    pub fn add_action(&mut self, mut action: Action) {
        let mut action_rect = self.core.rect.get();
        let action_text_width = action.text().len() as u32 * 8;
        if action_rect.width < action_text_width {
            // TODO: consider the icon width and some padding
            action_rect.width = action_text_width;
        }

        let mut y = action_rect.y + action_rect.height as i32;
        for entry in self.entries.iter() {
            let mut entry_rect = entry.rect().get();
            y += entry_rect.height as i32;

            if entry_rect.width < action_rect.width {
                entry_rect.width = action_rect.width;
                entry.rect().set(entry_rect);
            } else {
                action_rect.width = entry_rect.width;
            }
        }
        action_rect.y = y;
        action.rect().set(action_rect);
        self.entries.push(Box::new(action));
    }

    pub fn add_separator(&mut self) {
        let mut sep_rect = self.core.rect.get();

        let mut y = sep_rect.y + sep_rect.height as i32;
        for entry in self.entries.iter() {
            let entry_rect = entry.rect().get();
            y += entry_rect.height as i32;

            if entry_rect.width > sep_rect.width {
                sep_rect.width = entry_rect.width;
            }
        }
        sep_rect.y = y;

        let separator = Separator::new();
        separator.rect().set(sep_rect);
        self.entries.push(Box::new(separator));
    }

    pub fn text<S: Into<String>>(self, text: S) -> Self {
        self.text.set(text.into());
        self
    }

    pub fn text_offset(mut self, x: i32, y: i32) -> Self {
        self.text_offset = Point::new(x, y);
        self
    }
}

impl Click for Menu {
    fn emit_click(&self, point: Point) {
        if let Some(ref click_callback) = self.click_callback {
            click_callback(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(mut self, func: T) -> Self {
        self.click_callback = Some(Arc::new(func));

        self
    }
}

impl Placeable for Menu {}

impl Widget for Menu {
    fn rect(&self) -> &Cell<Rect> {
        &self.core.rect
    }

    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.core.rect.get();

        if self.activated.get() {
            renderer.rect(rect, self.bg_pressed);
        } else {
            renderer.rect(rect, self.core.bg);
        }

        let text = self.text.borrow();
        let mut point = self.text_offset;
        for c in text.chars() {
            if c == '\n' {
                point.x = 0;
                point.y += 16;
            } else {
                if point.x + 8 <= rect.width as i32 && point.y + 16 <= rect.height as i32 {
                    renderer.char(point + rect.point(), c, self.core.fg);
                }
                point.x += 8;
            }
        }

        renderer.rect(Rect::new(rect.x, rect.y + rect.height as i32 - 1, rect.width, 1), self.fg_border);

        if self.activated.get() {
            for entry in self.entries.iter() {
                entry.draw(renderer, _focused);
            }
        }
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        let mut ignore_event = false;
        if self.activated.get() {
            for entry in self.entries.iter() {
                if entry.event(event, focused, redraw) {
                    ignore_event = true;
                    self.pressed.set(true);
                }
            }
        }

        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;

                let rect = self.core.rect.get();
                if rect.contains(point) {
                    if left_button {
                        self.pressed.set(!self.pressed.get());

                        if self.activated.check_set(true) {
                            click = true;
                            *redraw = true;
                        }
                    } else {
                        if !self.pressed.get() {
                            if self.activated.check_set(false) {
                                click = true;
                                *redraw = true;
                            }
                        }
                    }
                } else {
                    if !ignore_event {
                        if left_button {
                            self.pressed.set(false);
                        } else {
                            if !self.pressed.get() {
                                if self.activated.check_set(false) {
                                    *redraw = true;
                                }
                            }
                        }
                    }
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
            _ => (),
        }
        focused
    }
}

impl Action {
    pub fn new<S: Into<String>>(text: S) -> Self {
        Action {
            core: WidgetCore::new()
                    .bg(Color::rgb(234, 234, 234)),
            text: CloneCell::new(text.into()),
            icon: None,
            bg_pressed: Color::rgb(210, 210, 208),
            text_offset: Point::default(),
            click_callback: None,
            pressed: Cell::new(false),
            hover: Cell::new(false),
        }
    }

    pub fn add_icon(mut self, icon: Image) -> Self {
        self.icon = Some(icon);
        self
    }

    pub fn text_offset(mut self, x: i32, y: i32) -> Self {
        self.text_offset = Point::new(x, y);
        self
    }
}

impl Click for Action {
    fn emit_click(&self, point: Point) {
        if let Some(ref click_callback) = self.click_callback {
            click_callback(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(mut self, func: T) -> Self {
        self.click_callback = Some(Arc::new(func));

        self
    }
}

impl Widget for Action {
    fn rect(&self) -> &Cell<Rect> {
        &self.core.rect
    }

    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.core.rect.get();

        if self.hover.get() {
            renderer.rect(rect, self.bg_pressed);
        } else {
            renderer.rect(rect, self.core.bg);
        }

        let text = self.text.borrow();
        let mut point = self.text_offset;
        for c in text.chars() {
            if c == '\n' {
                point.x = 0;
                point.y += 16;
            } else {
                if point.x + 8 <= rect.width as i32 && point.y + 16 <= rect.height as i32 {
                    renderer.char(point + rect.point(), c, self.core.fg);
                }
                point.x += 8;
            }
        }
    }

    fn event(&self, event: Event, _focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;
                let rect = self.core.rect.get();

                if rect.contains(point) {
                    if self.hover.check_set(true) {
                        *redraw = true;
                    }

                    if left_button {
                        if self.pressed.check_set(true) {
                            *redraw = true;
                        }
                    } else {
                        if self.pressed.check_set(false) {
                            click = true;
                            self.hover.set(false);
                            *redraw = true;
                        }
                    }
                } else {
                    if self.hover.check_set(false) {
                        *redraw = true;
                    }

                    if !left_button {
                        if self.pressed.check_set(false) {
                            *redraw = true;
                        }
                    }
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
            _ => (),
        }

        false
    }
}

impl Entry for Action {
    fn text(&mut self) -> String {
        self.text.get()
    }
}

impl Separator {
    pub fn new() -> Self {
        Separator {
            core: WidgetCore::new()
                    .bg(Color::rgb(234, 234, 234)),
        }
    }
}

impl Widget for Separator {
    fn rect(&self) -> &Cell<Rect> {
        &self.core.rect
    }

    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.core.rect.get();
        renderer.rect(rect, self.core.bg);

        let line_y = rect.y + rect.height as i32 / 2;
        let start = Point::new(rect.x, line_y);
        let end = Point::new(rect.x + rect.width as i32, line_y);
        renderer.line(start, end, self.core.fg);
    }

    fn event(&self, event: Event, _focused: bool, _redraw: &mut bool) -> bool {
        let mut ignore_event = false;
        match event {
            Event::Mouse { point, .. } => {
                let rect = self.core.rect.get();
                if rect.contains(point) {
                    ignore_event = true;
                }
            }
            _ => (),
        }
        ignore_event
    }
}

impl Entry for Separator {
    fn text(&mut self) -> String {
        String::new()
    }
}
<commit_msg>Adjust menu style<commit_after>extern crate orbimage;

use self::orbimage::Image;

use super::{CloneCell, Color, Event, Placeable, Point, Rect, Renderer, Widget, WidgetCore};
use super::callback::Click;
use super::cell::CheckSet;

use std::cell::Cell;
use std::sync::Arc;

pub struct Menu {
    pub core: WidgetCore,
    text: CloneCell<String>,
    bg_pressed: Color,
    fg_border: Color,
    text_offset: Point,
    entries: Vec<Box<Entry>>,
    click_callback: Option<Arc<Fn(&Menu, Point)>>,
    pressed: Cell<bool>,
    activated: Cell<bool>,
}

pub struct Separator {
    core: WidgetCore,
}

pub trait Entry: Widget {
    fn text(&mut self) -> String;
}

impl Menu {
    pub fn new<S: Into<String>>(name: S) -> Self {
        Menu {
            core: WidgetCore::new()
                    .bg(Color::rgb(234, 234, 234)),
            text: CloneCell::new(name.into()),
            bg_pressed: Color::rgb(210, 210, 208),
            fg_border: Color::rgb(209, 209, 208),
            text_offset: Point::default(),
            entries: Vec::with_capacity(10),
            click_callback: None,
            pressed: Cell::new(false),
            activated: Cell::new(false),
        }
    }

    pub fn add_action(&mut self, mut action: Action) {
        let mut action_rect = self.core.rect.get();
        let action_text_width = action.text().len() as u32 * 8;
        if action_rect.width < action_text_width {
            // TODO: consider the icon width and some padding
            action_rect.width = action_text_width;
        }

        let mut y = action_rect.y + action_rect.height as i32;
        for entry in self.entries.iter() {
            let mut entry_rect = entry.rect().get();
            y += entry_rect.height as i32;

            if entry_rect.width < action_rect.width {
                entry_rect.width = action_rect.width;
                entry.rect().set(entry_rect);
            } else {
                action_rect.width = entry_rect.width;
            }
        }
        action_rect.y = y;
        action.rect().set(action_rect);
        self.entries.push(Box::new(action));
    }

    pub fn add_separator(&mut self) {
        let mut sep_rect = self.core.rect.get();

        let mut y = sep_rect.y + sep_rect.height as i32;
        for entry in self.entries.iter() {
            let entry_rect = entry.rect().get();
            y += entry_rect.height as i32;

            if entry_rect.width > sep_rect.width {
                sep_rect.width = entry_rect.width;
            }
        }
        sep_rect.y = y;

        let separator = Separator::new();
        separator.rect().set(sep_rect);
        self.entries.push(Box::new(separator));
    }

    pub fn text<S: Into<String>>(self, text: S) -> Self {
        self.text.set(text.into());
        self
    }

    pub fn text_offset(mut self, x: i32, y: i32) -> Self {
        self.text_offset = Point::new(x, y);
        self
    }
}

impl Click for Menu {
    fn emit_click(&self, point: Point) {
        if let Some(ref click_callback) = self.click_callback {
            click_callback(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(mut self, func: T) -> Self {
        self.click_callback = Some(Arc::new(func));

        self
    }
}

impl Placeable for Menu {}

impl Widget for Menu {
    fn rect(&self) -> &Cell<Rect> {
        &self.core.rect
    }

    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.core.rect.get();

        if self.activated.get() {
            renderer.rect(rect, self.bg_pressed);
        } else {
            renderer.rect(rect, self.core.bg);
        }

        let text = self.text.borrow();
        let mut point = self.text_offset;
        for c in text.chars() {
            if c == '\n' {
                point.x = 0;
                point.y += 16;
            } else {
                if point.x + 8 <= rect.width as i32 && point.y + 16 <= rect.height as i32 {
                    renderer.char(point + rect.point(), c, self.core.fg);
                }
                point.x += 8;
            }
        }

        renderer.rect(Rect::new(rect.x, rect.y + rect.height as i32 - 1, rect.width, 1), self.fg_border);

        if self.activated.get() {
            for entry in self.entries.iter() {
                entry.draw(renderer, _focused);
            }
        }
    }

    fn event(&self, event: Event, focused: bool, redraw: &mut bool) -> bool {
        let mut ignore_event = false;
        if self.activated.get() {
            for entry in self.entries.iter() {
                if entry.event(event, focused, redraw) {
                    ignore_event = true;
                    self.pressed.set(true);
                }
            }
        }

        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;

                let rect = self.core.rect.get();
                if rect.contains(point) {
                    if left_button {
                        self.pressed.set(!self.pressed.get());

                        if self.activated.check_set(true) {
                            click = true;
                            *redraw = true;
                        }
                    } else {
                        if !self.pressed.get() {
                            if self.activated.check_set(false) {
                                click = true;
                                *redraw = true;
                            }
                        }
                    }
                } else {
                    if !ignore_event {
                        if left_button {
                            self.pressed.set(false);
                        } else {
                            if !self.pressed.get() {
                                if self.activated.check_set(false) {
                                    *redraw = true;
                                }
                            }
                        }
                    }
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
            _ => (),
        }
        focused
    }
}

pub struct Action {
    core: WidgetCore,
    text: CloneCell<String>,
    icon: Option<Image>,
    bg_pressed: Color,
    text_offset: Point,
    click_callback: Option<Arc<Fn(&Action, Point)>>,
    pressed: Cell<bool>,
    hover: Cell<bool>,
}

impl Action {
    pub fn new<S: Into<String>>(text: S) -> Self {
        Action {
            core: WidgetCore::new()
                    .bg(Color::rgb(255, 255, 255)),
            text: CloneCell::new(text.into()),
            icon: None,
            bg_pressed: Color::rgb(74, 144, 217),
            text_offset: Point::default(),
            click_callback: None,
            pressed: Cell::new(false),
            hover: Cell::new(false),
        }
    }

    pub fn add_icon(mut self, icon: Image) -> Self {
        self.icon = Some(icon);
        self
    }

    pub fn text_offset(mut self, x: i32, y: i32) -> Self {
        self.text_offset = Point::new(x, y);
        self
    }
}

impl Click for Action {
    fn emit_click(&self, point: Point) {
        if let Some(ref click_callback) = self.click_callback {
            click_callback(self, point);
        }
    }

    fn on_click<T: Fn(&Self, Point) + 'static>(mut self, func: T) -> Self {
        self.click_callback = Some(Arc::new(func));

        self
    }
}

impl Widget for Action {
    fn rect(&self) -> &Cell<Rect> {
        &self.core.rect
    }

    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.core.rect.get();

        let (bg, fg) = if self.hover.get() {
            (self.bg_pressed, self.core.bg)
        } else {
            (self.core.bg, self.core.fg)
        };

        renderer.rect(rect, bg);

        let text = self.text.borrow();
        let mut point = self.text_offset;
        for c in text.chars() {
            if c == '\n' {
                point.x = 0;
                point.y += 16;
            } else {
                if point.x + 8 <= rect.width as i32 && point.y + 16 <= rect.height as i32 {
                    renderer.char(point + rect.point(), c, fg);
                }
                point.x += 8;
            }
        }
    }

    fn event(&self, event: Event, _focused: bool, redraw: &mut bool) -> bool {
        match event {
            Event::Mouse { point, left_button, .. } => {
                let mut click = false;
                let rect = self.core.rect.get();

                if rect.contains(point) {
                    if self.hover.check_set(true) {
                        *redraw = true;
                    }

                    if left_button {
                        if self.pressed.check_set(true) {
                            *redraw = true;
                        }
                    } else {
                        if self.pressed.check_set(false) {
                            click = true;
                            self.hover.set(false);
                            *redraw = true;
                        }
                    }
                } else {
                    if self.hover.check_set(false) {
                        *redraw = true;
                    }

                    if !left_button {
                        if self.pressed.check_set(false) {
                            *redraw = true;
                        }
                    }
                }

                if click {
                    let click_point: Point = point - rect.point();
                    self.emit_click(click_point);
                }
            }
            _ => (),
        }

        false
    }
}

impl Entry for Action {
    fn text(&mut self) -> String {
        self.text.get()
    }
}

impl Separator {
    pub fn new() -> Self {
        Separator {
            core: WidgetCore::new()
                    .bg(Color::rgb(255, 255, 255)),
        }
    }
}

impl Widget for Separator {
    fn rect(&self) -> &Cell<Rect> {
        &self.core.rect
    }

    fn draw(&self, renderer: &mut Renderer, _focused: bool) {
        let rect = self.core.rect.get();
        renderer.rect(rect, self.core.bg);

        let line_y = rect.y + rect.height as i32 / 2;
        let start = Point::new(rect.x, line_y);
        let end = Point::new(rect.x + rect.width as i32, line_y);
        renderer.line(start, end, self.core.fg);
    }

    fn event(&self, event: Event, _focused: bool, _redraw: &mut bool) -> bool {
        let mut ignore_event = false;
        match event {
            Event::Mouse { point, .. } => {
                let rect = self.core.rect.get();
                if rect.contains(point) {
                    ignore_event = true;
                }
            }
            _ => (),
        }
        ignore_event
    }
}

impl Entry for Separator {
    fn text(&mut self) -> String {
        String::new()
    }
}
<|endoftext|>


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

#[derive(Debug)]
enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(u32),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    Comment,
}

#[derive(Debug)]
struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}
impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn peek(&self) -> char() {
        if (self.is_at_end()) {
            '\0'
        } else {
            self.char_at(self.current)
        }
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.char_at(self.current - 1)
    }

    fn is_match(&mut self, expected: char) -> bool {
        if (self.is_at_end()) {
            return false;
        }
        if (self.char_at(self.current) != expected) {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.source.chars().skip(self.start).take(self.current - self.start).collect(),
            line: self.line,
        }
    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.is_match('/') {
                    // Comments go on till the end of the line
                    while (self.peek() != '\n' && !self.is_at_end()) {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            _ => {
                return Err(format!("Unexpected character at line {}, pos {}",
                                   self.line,
                                   self.current))
            }
        };
        Ok(self.add_context(token))
    }
}

fn tokenize(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        match scanner.scan_next() {
            Ok(token) => tokens.push(token),
            Err(message) => return Err(message),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

fn run(source: &String) -> Result<(), String> {
    let tokens = try!(tokenize(source));
    for token in tokens {
        println!("{:?}", token);
    }
    Ok(())
}

fn run_file(file_name: &String) -> Result<(), String> {
    match File::open(file_name) {
        Err(_) => {
            Err("Error opening file".into()) // TODO: add context
        }
        Ok(mut file) => {
            let mut source = String::new();
            match file.read_to_string(&mut source) {
                Err(_) => {
                    Err("Error reading file".into()) // TODO: add context
                }
                Ok(_) => run(&source),
            }
        }
    }
}

fn run_prompt() -> Result<(), String> {
    loop {
        print!("> ");
        io::stdout().flush();
        let mut source = String::new();
        io::stdin().read_line(&mut source);
        try!(run(&source));
        ()
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let result = match args.len() {
        // The first argument is the program name
        1 => run_prompt(),
        2 => run_file(&args[1]),
        _ => {
            println!("Usage: rulox [script]");
            Ok(())
        }
    };
    let exit_code = match result {
        Ok(_) => 0,
        Err(e) => {
            println!("{}", e);
            1
        }
    };
    std::process::exit(exit_code)
}
<commit_msg>Whitespaces<commit_after>use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;

#[derive(Debug)]
enum Token {
    LeftParen,
    RightParen,
    LeftBrace,
    RightBrace,
    Comma,
    Dot,
    Minus,
    Plus,
    Semicolon,
    Slash,
    Star,
    Bang,
    BangEqual,
    Equal,
    EqualEqual,
    Greater,
    GreaterEqual,
    Less,
    LessEqual,
    Identifier(String),
    StringLiteral(String),
    NumberLiteral(u32),
    // Keywords
    And,
    Class,
    Else,
    False,
    Fun,
    For,
    If,
    Nil,
    Or,
    Print,
    Return,
    Super,
    This,
    True,
    Var,
    While,

    Eof,
    // These are not described in the book but they simplify the parser
    Comment,
    Whitespace,
}

#[derive(Debug)]
struct TokenWithContext {
    token: Token,
    lexeme: String, // TODO: make a reference
    line: usize,
}

struct Scanner {
    start: usize,
    current: usize,
    line: usize,
    source: String, // TODO: make a reference
}
impl Scanner {
    fn initialize(source: &String) -> Scanner {
        Scanner {
            start: 0,
            current: 0,
            line: 0,
            source: source.clone(),
        }
    }

    fn is_at_end(&self) -> bool {
        self.current >= self.source.len()
    }

    fn char_at(&self, index: usize) -> char {
        // TODO: there must be a better way
        self.source.chars().nth(index).unwrap()
    }

    fn peek(&self) -> char() {
        if (self.is_at_end()) {
            '\0'
        } else {
            self.char_at(self.current)
        }
    }

    fn advance(&mut self) -> char {
        self.current += 1;
        self.char_at(self.current - 1)
    }

    fn is_match(&mut self, expected: char) -> bool {
        if (self.is_at_end()) {
            return false;
        }
        if (self.char_at(self.current) != expected) {
            return false;
        }
        self.current += 1;
        return true;
    }

    fn add_context(&self, token: Token) -> TokenWithContext {
        TokenWithContext {
            token: token,
            lexeme: self.source.chars().skip(self.start).take(self.current - self.start).collect(),
            line: self.line,
        }
    }

    fn scan_next(&mut self) -> Result<TokenWithContext, String> {
        self.start = self.current;
        let token = match self.advance() {
            '(' => Token::LeftParen,
            ')' => Token::RightParen,
            '{' => Token::LeftBrace,
            '}' => Token::RightBrace,
            ',' => Token::Comma,
            '.' => Token::Dot,
            '-' => Token::Minus,
            '+' => Token::Plus,
            ';' => Token::Semicolon,
            '*' => Token::Star,
            '!' => {
                if self.is_match('=') {
                    Token::BangEqual
                } else {
                    Token::Bang
                }
            }
            '=' => {
                if self.is_match('=') {
                    Token::EqualEqual
                } else {
                    Token::Equal
                }
            }
            '<' => {
                if self.is_match('=') {
                    Token::LessEqual
                } else {
                    Token::Less
                }
            }
            '>' => {
                if self.is_match('=') {
                    Token::GreaterEqual
                } else {
                    Token::Greater
                }
            }
            '/' => {
                if self.is_match('/') {
                    // Comments go on till the end of the line
                    while (self.peek() != '\n' && !self.is_at_end()) {
                        self.advance();
                    }
                    Token::Comment
                } else {
                    Token::Slash
                }
            }
            ' ' => Token::Whitespace,
            '\r' => Token::Whitespace,
            '\t' => Token::Whitespace,
            '\n' => {
                self.line += 1;
                Token::Whitespace
            }
            _ => {
                return Err(format!("Unexpected character at line {}, pos {}",
                                   self.line,
                                   self.current))
            }
        };
        Ok(self.add_context(token))
    }
}

fn tokenize(source: &String) -> Result<Vec<TokenWithContext>, String> {
    let mut scanner = Scanner::initialize(source);
    let mut tokens = Vec::new();
    while !scanner.is_at_end() {
        match scanner.scan_next() {
            Ok(tokenWithContext) => {
                match tokenWithContext.token {
                    // Ignoring tokens we don't care about
                    Token::Comment => {}
                    Token::Whitespace => {}
                    _ => tokens.push(tokenWithContext),
                }
            }
            Err(message) => return Err(message),
        }
    }
    tokens.push(TokenWithContext {
        token: Token::Eof,
        lexeme: "".into(),
        line: scanner.line,
    });
    Ok(tokens)
}

fn run(source: &String) -> Result<(), String> {
    let tokens = try!(tokenize(source));
    for token in tokens {
        println!("{:?}", token);
    }
    Ok(())
}

fn run_file(file_name: &String) -> Result<(), String> {
    match File::open(file_name) {
        Err(_) => {
            Err("Error opening file".into()) // TODO: add context
        }
        Ok(mut file) => {
            let mut source = String::new();
            match file.read_to_string(&mut source) {
                Err(_) => {
                    Err("Error reading file".into()) // TODO: add context
                }
                Ok(_) => run(&source),
            }
        }
    }
}

fn run_prompt() -> Result<(), String> {
    loop {
        print!("> ");
        io::stdout().flush();
        let mut source = String::new();
        io::stdin().read_line(&mut source);
        try!(run(&source));
        ()
    }
}

fn main() {
    let args: Vec<String> = env::args().collect();
    let result = match args.len() {
        // The first argument is the program name
        1 => run_prompt(),
        2 => run_file(&args[1]),
        _ => {
            println!("Usage: rulox [script]");
            Ok(())
        }
    };
    let exit_code = match result {
        Ok(_) => 0,
        Err(e) => {
            println!("{}", e);
            1
        }
    };
    std::process::exit(exit_code)
}
<|endoftext|>


<commit_before>#[macro_use] extern crate tera;

#[macro_use] extern crate lazy_static;

extern crate iron;
extern crate router;
extern crate hyper;
extern crate regex;

use std::env;
use std::io::prelude::*;
use std::fs::File;

use tera::Tera;
use tera::Context;

use regex::Regex;

use iron::prelude::*;

use iron::{BeforeMiddleware, AroundMiddleware, AfterMiddleware, Handler};

use router::{Router, NoRoute};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};

use std::error::Error;
use std::fmt::{self, Debug};

#[derive(Debug)]
struct StringError(String);

impl fmt::Display for StringError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self, f)
    }
}

impl Error for StringError {
    fn description(&self) -> &str { &*self.0 }
}

#[derive(Default,Clone)]
struct Spaceship {
    cache: Arc<RwLock<HashMap<String, String>>>,
    tera: Arc<RwLock<Tera>>,
}

impl Spaceship {
	fn new() -> Spaceship {
		Spaceship{tera: Arc::new(RwLock::new(compile_templates!("templates/**/*"))), ..Default::default()}
	}
	fn tera<'a>(&'a self) -> &'a Arc<RwLock<Tera>> {
		&self.tera
	}
	fn cache<'a>(&'a self) -> &'a Arc<RwLock<HashMap<String, String>>> {
		&self.cache
	}

	fn render(&self, template: &String, ctx: Context) -> Option<String> {
		match self.tera().read() {
			Ok(x) => {
				x.render(template, ctx).and_then(|content| {Ok(content)}).ok()
				},
			Err(_) => None,
		}
	}
}

impl Handler for Spaceship {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let mut chunks = req.url.path();
		chunks.retain(|&x| x != "");
		let path = chunks.join("/");

		let mut ctx = Context::new();
		ctx.add("title", &"spaceship!");
		ctx.add("body", &format!("\"{}\" is still work in progress", path));

		let template = String::from("index.html");
		match self.render(&template, ctx) {
			Some(content) => {
				let mut resp = Response::with((iron::status::Ok, content));
				resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
				return Ok(resp);
			},
			None => {},
		}
		Err(IronError::new(StringError("Error".to_string()), iron::status::InternalServerError))
	}
}

impl AfterMiddleware for Spaceship {
    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<Response> {
		let mut chunks = req.url.path();
		chunks.retain(|&x| x != "");
		let path = chunks.join("/");

		let mut ctx = Context::new();
		ctx.add("title", &"spaceship!");
		ctx.add("body", &"404");

		let template = String::from("404.html");
		match self.render(&template, ctx) {
			Some(content) => {
				let mut resp = Response::with((iron::status::Ok, content));
				resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
				return Ok(resp);
			},
			None => {
				return Ok(Response::with((iron::status::InternalServerError)));
			}
		}

    }
}

#[derive(Default,Clone)]
struct CssHandler {
    cache: Arc<RwLock<HashMap<String, String>>>
}

impl CssHandler {
	fn new() -> CssHandler {
		CssHandler { ..Default::default()}
	}

	fn read_css_to_string(&self, path: &String) -> Option<String> {
		lazy_static! {
			static ref CSS_REGEX: Regex = Regex::new(r".*\.css").unwrap();
		}
		if CSS_REGEX.is_match(path) {
			return File::open(path).and_then(|mut f| {
						let mut s = String::new();
						f.read_to_string(&mut s).and_then(|_| {Ok(s)})
					}).ok();
		}
		None
	}
}

impl Handler for CssHandler {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let mut chunks = req.url.path();
		chunks.retain(|&x| x != "");
		let path = chunks.join("/");
		println!("lookup {:?} {}", chunks, path);
		match self.read_css_to_string(&String::from(path)) {
			Some(content) => {
				let mut resp = Response::with((iron::status::Ok, content));
				resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Css, vec![])));
				return Ok(resp);
			},
			None => {}		}
		Err(IronError::new(StringError("Error".to_string()), iron::status::BadRequest))
}
}

impl AroundMiddleware for CssHandler {
    fn around(self, handler: Box<Handler>) -> Box<Handler> {
		Box::new(CssHandler::new()) as Box<Handler>
    }
}

fn main() {
	let address = env::var("SPACESHIP_ADDRESS").unwrap_or(String::from("0.0.0.0"));
	let port = env::var("SPACESHIP_PORT").unwrap_or(String::from("8080"));

    let mut router = Router::new();

	let css_handler = CssHandler::new();
    router.get("/static/*", css_handler, "css");

    let spaceship_handler = Spaceship::new();
    // router.get("/[^static]*", spaceship_handler.clone(), "spaceship");
    router.get("/", spaceship_handler.clone(), "index");

    let mut chain = Chain::new(router);
    // chain.link_before(MyMiddleware);
    chain.link_after(spaceship_handler.clone());

    Iron::new(chain).http(format!("{}:{}",address,port)).unwrap();
}
<commit_msg>chore/fix: adjust to tera API change<commit_after>#[macro_use] extern crate tera;

#[macro_use] extern crate lazy_static;

extern crate iron;
extern crate router;
extern crate hyper;
extern crate regex;

use std::env;
use std::io::prelude::*;
use std::fs::File;

use tera::Tera;
use tera::Context;

use regex::Regex;

use iron::prelude::*;

use iron::{BeforeMiddleware, AroundMiddleware, AfterMiddleware, Handler};

use router::{Router, NoRoute};
use std::collections::HashMap;
use std::sync::{Arc, RwLock};

use hyper::header::ContentType;
use hyper::mime::{Mime, TopLevel, SubLevel};

use std::error::Error;
use std::fmt::{self, Debug};

#[derive(Debug)]
struct StringError(String);

impl fmt::Display for StringError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        Debug::fmt(self, f)
    }
}

impl Error for StringError {
    fn description(&self) -> &str { &*self.0 }
}

#[derive(Default,Clone)]
struct Spaceship {
    cache: Arc<RwLock<HashMap<String, String>>>,
    tera: Arc<RwLock<Tera>>,
}

impl Spaceship {
	fn new() -> Spaceship {
		Spaceship{tera: Arc::new(RwLock::new(compile_templates!("templates/**/*"))), ..Default::default()}
	}
	fn tera<'a>(&'a self) -> &'a Arc<RwLock<Tera>> {
		&self.tera
	}
	fn cache<'a>(&'a self) -> &'a Arc<RwLock<HashMap<String, String>>> {
		&self.cache
	}

	fn render(&self, template: &String, ctx: &mut Context) -> Option<String> {
		match self.tera().read() {
			Ok(x) => {
				x.render(template, ctx).and_then(|content| {Ok(content)}).ok()
				},
			Err(_) => None,
		}
	}
}

impl Handler for Spaceship {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let mut chunks = req.url.path();
		chunks.retain(|&x| x != "");
		let path = chunks.join("/");

		let mut ctx = Context::new();
		ctx.add("title", &"spaceship!");
		ctx.add("body", &format!("\"{}\" is still work in progress", path));

		let template = String::from("index.html");
		match self.render(&template, &mut ctx) {
			Some(content) => {
				let mut resp = Response::with((iron::status::Ok, content));
				resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
				return Ok(resp);
			},
			None => {},
		}
		Err(IronError::new(StringError("Error".to_string()), iron::status::InternalServerError))
	}
}

impl AfterMiddleware for Spaceship {
    fn catch(&self, req: &mut Request, err: IronError) -> IronResult<Response> {
		let mut chunks = req.url.path();
		chunks.retain(|&x| x != "");
		let path = chunks.join("/");

		let mut ctx = Context::new();
		ctx.add("title", &"spaceship!");
		ctx.add("body", &"404");

		let template = String::from("404.html");
		match self.render(&template, &mut ctx) {
			Some(content) => {
				let mut resp = Response::with((iron::status::Ok, content));
				resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Html, vec![])));
				return Ok(resp);
			},
			None => {
				return Ok(Response::with((iron::status::InternalServerError)));
			}
		}

    }
}

#[derive(Default,Clone)]
struct CssHandler {
    cache: Arc<RwLock<HashMap<String, String>>>
}

impl CssHandler {
	fn new() -> CssHandler {
		CssHandler { ..Default::default()}
	}

	fn read_css_to_string(&self, path: &String) -> Option<String> {
		lazy_static! {
			static ref CSS_REGEX: Regex = Regex::new(r".*\.css").unwrap();
		}
		if CSS_REGEX.is_match(path) {
			return File::open(path).and_then(|mut f| {
						let mut s = String::new();
						f.read_to_string(&mut s).and_then(|_| {Ok(s)})
					}).ok();
		}
		None
	}
}

impl Handler for CssHandler {
	fn handle(&self, req: &mut Request) -> IronResult<Response> {
		let mut chunks = req.url.path();
		chunks.retain(|&x| x != "");
		let path = chunks.join("/");
		println!("lookup {:?} {}", chunks, path);
		match self.read_css_to_string(&String::from(path)) {
			Some(content) => {
				let mut resp = Response::with((iron::status::Ok, content));
				resp.headers.set(ContentType(Mime(TopLevel::Text, SubLevel::Css, vec![])));
				return Ok(resp);
			},
			None => {}		}
		Err(IronError::new(StringError("Error".to_string()), iron::status::BadRequest))
}
}

impl AroundMiddleware for CssHandler {
    fn around(self, handler: Box<Handler>) -> Box<Handler> {
		Box::new(CssHandler::new()) as Box<Handler>
    }
}

fn main() {
	let address = env::var("SPACESHIP_ADDRESS").unwrap_or(String::from("0.0.0.0"));
	let port = env::var("SPACESHIP_PORT").unwrap_or(String::from("8080"));

    let mut router = Router::new();

	let css_handler = CssHandler::new();
    router.get("/static/*", css_handler, "css");

    let spaceship_handler = Spaceship::new();
    // router.get("/[^static]*", spaceship_handler.clone(), "spaceship");
    router.get("/", spaceship_handler.clone(), "index");

    let mut chain = Chain::new(router);
    // chain.link_before(MyMiddleware);
    chain.link_after(spaceship_handler.clone());

    Iron::new(chain).http(format!("{}:{}",address,port)).unwrap();
}
<|endoftext|>


<commit_before>

use sound_stream_settings::SoundStreamSettings;
use std::fmt::Show;

/// The DSP Node contains a vector of children
/// nodes (within the `MixerInput`s), from which
/// audio can be requested as well as the current
/// SoundStream settings.
#[deriving(Show, Clone, Encodable, Decodable)]
pub struct Data {
    /// SoundStreamSettings for buffer calculations.
    pub settings: SoundStreamSettings,
    /// Master volume for DSP node.
    pub vol: f32,
    /// Panning for DSP node.
    pub pan: f32
}

impl Data {
    /// Default constructor for a Data struct.
    pub fn new(settings: SoundStreamSettings) -> Data {
        Data {
            settings: settings,
            vol: 1f32,
            pan: 0f32,
        }
    }
}

/// DSP Node trait. Implement this for any audio
/// instrument or effects types. Be sure to add
/// the `Node` struct to a field as well, and
/// override the `get_node` and `get_node_mut`
/// methods by returning a ref (/mut) to it.
pub trait Node: Clone + Show {

    /// Return a reference a Data struct owned by the Node.
    fn get_node_data<'a>(&'a mut self) -> &'a mut Data;
    /// Return a reference to the inputs for the Node.
    fn get_inputs<'a>(&'a self) -> Vec<&'a Node> { Vec::new() }
    /// Return a mutable reference to the inputs for the Node.
    fn get_inputs_mut<'a>(&'a mut self) -> Vec<&'a mut Node> { Vec::new() }

    /// Apply settings to self and all inputs.
    fn apply_settings(&mut self, settings: SoundStreamSettings) {
        for input in self.get_inputs_mut().mut_iter() {
            input.apply_settings(settings);
        }
        self.get_node_data().settings = settings;
    }

    /// Remove all inputs from the `inputs` vector.
    fn remove_all_inputs(&mut self) {
        self.get_inputs_mut().clear();
    }

    /// Receive incoming audio (override this
    /// to do something with the input).
    fn audio_received(&mut self, _input: &Vec<f32>) {}

    /// Request audio from inputs, process and
    /// pass back to the output! Override this
    /// method for any synthesis or generative
    /// types.
    fn audio_requested<'a>(&'a mut self, output: &mut Vec<f32>) {
        let frames = self.get_node_data().settings.frames as uint;
        let channels = self.get_node_data().settings.channels as uint;
        {
            let mut inputs = self.get_inputs_mut();
            let mut vol_per_channel: Vec<f32> = Vec::from_elem(channels, 1f32);
            for i in range(0, inputs.len()) {
                let input = inputs.get_mut(i);
                let input_vol = input.get_node_data().vol;
                let input_pan = input.get_node_data().pan;
                let mut working: Vec<f32> = Vec::from_elem((frames * channels) as uint, 0f32);
                // Call audio_requested for each input.
                input.audio_requested(&mut working);
                // Construct precalculated volume and
                // pan array (for efficiency).
                let vol_l: f32 = input_vol * (1f32 - input_pan);
                let vol_r: f32 = input_vol * input_pan;
                for j in range(0, vol_per_channel.len()) {
                    *vol_per_channel.get_mut(j) = if j == 0 { vol_l } else { vol_r };
                }
                // Sum all input nodes to output (considering
                // pan, vol and interleaving).
                for j in range(0, frames) {
                    for k in range(0, channels) {
                        *output.get_mut(j * channels + k) +=
                            working[j * channels + k] * vol_per_channel[k];
                    }
                }
            }
        }
        // Custom buffer processing.
        self.process_buffer(output);
    }

    /// Override for custom processing of audio per
    /// buffer. This is mainly for audio effects. Get's
    /// called at the end of audio_requested.
    fn process_buffer(&mut self, _output: &mut Vec<f32>) {}

}

<commit_msg>Remove derivation of Show and Clone from Node trait<commit_after>

use sound_stream_settings::SoundStreamSettings;
use std::fmt::Show;

/// The DSP Node contains a vector of children
/// nodes (within the `MixerInput`s), from which
/// audio can be requested as well as the current
/// SoundStream settings.
#[deriving(Show, Clone, Encodable, Decodable)]
pub struct Data {
    /// SoundStreamSettings for buffer calculations.
    pub settings: SoundStreamSettings,
    /// Master volume for DSP node.
    pub vol: f32,
    /// Panning for DSP node.
    pub pan: f32
}

impl Data {
    /// Default constructor for a Data struct.
    pub fn new(settings: SoundStreamSettings) -> Data {
        Data {
            settings: settings,
            vol: 1f32,
            pan: 0f32,
        }
    }
}

/// DSP Node trait. Implement this for any audio
/// instrument or effects types. Be sure to add
/// the `Node` struct to a field as well, and
/// override the `get_node` and `get_node_mut`
/// methods by returning a ref (/mut) to it.
pub trait Node {

    /// Return a reference a Data struct owned by the Node.
    fn get_node_data<'a>(&'a mut self) -> &'a mut Data;
    /// Return a reference to the inputs for the Node.
    fn get_inputs<'a>(&'a self) -> Vec<&'a Node> { Vec::new() }
    /// Return a mutable reference to the inputs for the Node.
    fn get_inputs_mut<'a>(&'a mut self) -> Vec<&'a mut Node> { Vec::new() }

    /// Apply settings to self and all inputs.
    fn apply_settings(&mut self, settings: SoundStreamSettings) {
        for input in self.get_inputs_mut().mut_iter() {
            input.apply_settings(settings);
        }
        self.get_node_data().settings = settings;
    }

    /// Remove all inputs from the `inputs` vector.
    fn remove_all_inputs(&mut self) {
        self.get_inputs_mut().clear();
    }

    /// Receive incoming audio (override this
    /// to do something with the input).
    fn audio_received(&mut self, _input: &Vec<f32>) {}

    /// Request audio from inputs, process and
    /// pass back to the output! Override this
    /// method for any synthesis or generative
    /// types.
    fn audio_requested<'a>(&'a mut self, output: &mut Vec<f32>) {
        let frames = self.get_node_data().settings.frames as uint;
        let channels = self.get_node_data().settings.channels as uint;
        {
            let mut inputs = self.get_inputs_mut();
            let mut vol_per_channel: Vec<f32> = Vec::from_elem(channels, 1f32);
            for i in range(0, inputs.len()) {
                let input = inputs.get_mut(i);
                let input_vol = input.get_node_data().vol;
                let input_pan = input.get_node_data().pan;
                let mut working: Vec<f32> = Vec::from_elem((frames * channels) as uint, 0f32);
                // Call audio_requested for each input.
                input.audio_requested(&mut working);
                // Construct precalculated volume and
                // pan array (for efficiency).
                let vol_l: f32 = input_vol * (1f32 - input_pan);
                let vol_r: f32 = input_vol * input_pan;
                for j in range(0, vol_per_channel.len()) {
                    *vol_per_channel.get_mut(j) = if j == 0 { vol_l } else { vol_r };
                }
                // Sum all input nodes to output (considering
                // pan, vol and interleaving).
                for j in range(0, frames) {
                    for k in range(0, channels) {
                        *output.get_mut(j * channels + k) +=
                            working[j * channels + k] * vol_per_channel[k];
                    }
                }
            }
        }
        // Custom buffer processing.
        self.process_buffer(output);
    }

    /// Override for custom processing of audio per
    /// buffer. This is mainly for audio effects. Get's
    /// called at the end of audio_requested.
    fn process_buffer(&mut self, _output: &mut Vec<f32>) {}

}

<|endoftext|>


<commit_before>use anyhow::Result;
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 histogram::Histogram;
use pinger::{ping, PingResult};
use std::io;
use std::io::Write;
use std::ops::Add;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Modifier, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset, GraphType, Paragraph};
use tui::{symbols, Terminal};

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.")]
struct Args {
    #[structopt(help = "Host or IP to ping")]
    host: String,
    #[structopt(
        short,
        long,
        help = "Determines the number pings to display. Defaults to 100"
    )]
    buffer: Option<usize>,
}

struct App {
    data: Vec<(f64, f64)>,
    capacity: usize,
    idx: i64,
    window: [f64; 2],
}

impl App {
    fn new(capacity: usize) -> Self {
        App {
            data: Vec::with_capacity(capacity),
            capacity,
            idx: 0,
            window: [0.0, capacity as f64],
        }
    }
    fn update(&mut self, item: Option<Duration>) {
        self.idx += 1;
        if self.data.len() >= self.capacity {
            self.data.remove(0);
            self.window[0] += 1_f64;
            self.window[1] += 1_f64;
        }
        match item {
            Some(dur) => self.data.push((self.idx as f64, dur.as_micros() as f64)),
            None => self.data.push((self.idx as f64, 0_f64)),
        }
    }
    fn stats(&self) -> Histogram {
        let mut hist = Histogram::new();

        for (_, val) in self.data.iter().filter(|v| v.1 != 0f64) {
            hist.increment(*val as u64).unwrap_or(());
        }

        hist
    }
    fn y_axis_bounds(&self) -> [f64; 2] {
        let min = self
            .data
            .iter()
            .map(|v| v.1)
            .fold(f64::INFINITY, |a, b| a.min(b));
        let max = self.data.iter().map(|v| v.1).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 y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Split into 5 sections
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let increment = Duration::from_micros((difference / 3f64) as u64);
        let duration = Duration::from_micros(min as u64);

        vec![
            Span::styled(
                format!("{:?}", duration),
                Style::default().add_modifier(Modifier::BOLD),
            ),
            Span::raw(format!("{:?}", duration.add(increment))),
            Span::raw(format!("{:?}", duration.add(increment) * 2)),
            Span::raw(format!("{:?}", duration.add(increment) * 3)),
            Span::styled(
                format!("{:?}", duration.add(increment) * 4),
                Style::default().add_modifier(Modifier::BOLD),
            ),
        ]
    }
}

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

fn main() -> Result<()> {
    let args = Args::from_args();
    let mut app = App::new(args.buffer.unwrap_or(100));
    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 ping_tx = key_tx.clone();

    let host = args.host.clone();

    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping(host).expect("Error pinging");

        loop {
            ping_tx.send(Event::Update(stream.recv()?))?;
        }
    });

    // Pump keyboard messages into the queue
    thread::spawn(move || -> Result<()> {
        loop {
            if event::poll(Duration::from_secs(1)).unwrap() {
                if let CEvent::Key(key) = event::read().unwrap() {
                    key_tx.send(Event::Input(key)).unwrap();
                }
            }
        }
    });

    loop {
        match rx.recv()? {
            Event::Update(ping_result) => {
                match ping_result {
                    PingResult::Pong(duration) => app.update(Some(duration)),
                    PingResult::Timeout => app.update(None),
                };
                terminal.draw(|f| {
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(2)
                        .constraints(
                            [Constraint::Percentage(5), Constraint::Percentage(10)].as_ref(),
                        )
                        .split(f.size());

                    let header_layout = Layout::default()
                        .direction(Direction::Horizontal)
                        .constraints(
                            [
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                            ]
                            .as_ref(),
                        )
                        .split(chunks[0]);

                    f.render_widget(
                        Paragraph::new(format!("Pinging {}", &args.host)),
                        header_layout[0],
                    );

                    let stats = app.stats();

                    f.render_widget(
                        Paragraph::new(format!(
                            "min {:?}",
                            Duration::from_micros(stats.minimum().unwrap_or(0))
                        )),
                        header_layout[1],
                    );
                    f.render_widget(
                        Paragraph::new(format!(
                            "max {:?}",
                            Duration::from_micros(stats.maximum().unwrap_or(0))
                        )),
                        header_layout[2],
                    );
                    f.render_widget(
                        Paragraph::new(format!(
                            "p95 {:?}",
                            Duration::from_micros(stats.percentile(95.0).unwrap_or(0))
                        )),
                        header_layout[3],
                    );

                    let dataset = Dataset::default()
                        .marker(symbols::Marker::Braille)
                        .style(Style::default().fg(Color::Cyan))
                        .graph_type(GraphType::Line)
                        .data(&app.data);

                    let y_axis_bounds = app.y_axis_bounds();

                    let chart = Chart::new(vec![dataset])
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(app.window),
                        )
                        .y_axis(
                            Axis::default()
                                .title("Time")
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );
                    f.render_widget(chart, chunks[1]);
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    break;
                }
                _ => {}
            },
        }
    }

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

    Ok(())
}
<commit_msg>Use default_value<commit_after>use anyhow::Result;
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 histogram::Histogram;
use pinger::{ping, PingResult};
use std::io;
use std::io::Write;
use std::ops::Add;
use std::sync::mpsc;
use std::thread;
use std::time::Duration;
use structopt::StructOpt;
use tui::backend::CrosstermBackend;
use tui::layout::{Constraint, Direction, Layout};
use tui::style::{Color, Modifier, Style};
use tui::text::Span;
use tui::widgets::{Axis, Block, Borders, Chart, Dataset, GraphType, Paragraph};
use tui::{symbols, Terminal};

#[derive(Debug, StructOpt)]
#[structopt(name = "gping", about = "Ping, but with a graph.")]
struct Args {
    #[structopt(help = "Host or IP to ping")]
    host: String,
    #[structopt(
        short,
        long,
        default_value = "100",
        help = "Determines the number pings to display."
    )]
    buffer: usize,
}

struct App {
    data: Vec<(f64, f64)>,
    capacity: usize,
    idx: i64,
    window: [f64; 2],
}

impl App {
    fn new(capacity: usize) -> Self {
        App {
            data: Vec::with_capacity(capacity),
            capacity,
            idx: 0,
            window: [0.0, capacity as f64],
        }
    }
    fn update(&mut self, item: Option<Duration>) {
        self.idx += 1;
        if self.data.len() >= self.capacity {
            self.data.remove(0);
            self.window[0] += 1_f64;
            self.window[1] += 1_f64;
        }
        match item {
            Some(dur) => self.data.push((self.idx as f64, dur.as_micros() as f64)),
            None => self.data.push((self.idx as f64, 0_f64)),
        }
    }
    fn stats(&self) -> Histogram {
        let mut hist = Histogram::new();

        for (_, val) in self.data.iter().filter(|v| v.1 != 0f64) {
            hist.increment(*val as u64).unwrap_or(());
        }

        hist
    }
    fn y_axis_bounds(&self) -> [f64; 2] {
        let min = self
            .data
            .iter()
            .map(|v| v.1)
            .fold(f64::INFINITY, |a, b| a.min(b));
        let max = self.data.iter().map(|v| v.1).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 y_axis_labels(&self, bounds: [f64; 2]) -> Vec<Span> {
        // Split into 5 sections
        let min = bounds[0];
        let max = bounds[1];

        let difference = max - min;
        let increment = Duration::from_micros((difference / 3f64) as u64);
        let duration = Duration::from_micros(min as u64);

        vec![
            Span::styled(
                format!("{:?}", duration),
                Style::default().add_modifier(Modifier::BOLD),
            ),
            Span::raw(format!("{:?}", duration.add(increment))),
            Span::raw(format!("{:?}", duration.add(increment) * 2)),
            Span::raw(format!("{:?}", duration.add(increment) * 3)),
            Span::styled(
                format!("{:?}", duration.add(increment) * 4),
                Style::default().add_modifier(Modifier::BOLD),
            ),
        ]
    }
}

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

fn main() -> Result<()> {
    let args = Args::from_args();
    let mut app = App::new(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 ping_tx = key_tx.clone();

    let host = args.host.clone();

    // Pump ping messages into the queue
    thread::spawn(move || -> Result<()> {
        let stream = ping(host).expect("Error pinging");

        loop {
            ping_tx.send(Event::Update(stream.recv()?))?;
        }
    });

    // Pump keyboard messages into the queue
    thread::spawn(move || -> Result<()> {
        loop {
            if event::poll(Duration::from_secs(1)).unwrap() {
                if let CEvent::Key(key) = event::read().unwrap() {
                    key_tx.send(Event::Input(key)).unwrap();
                }
            }
        }
    });

    loop {
        match rx.recv()? {
            Event::Update(ping_result) => {
                match ping_result {
                    PingResult::Pong(duration) => app.update(Some(duration)),
                    PingResult::Timeout => app.update(None),
                };
                terminal.draw(|f| {
                    let chunks = Layout::default()
                        .direction(Direction::Vertical)
                        .margin(2)
                        .constraints(
                            [Constraint::Percentage(5), Constraint::Percentage(10)].as_ref(),
                        )
                        .split(f.size());

                    let header_layout = Layout::default()
                        .direction(Direction::Horizontal)
                        .constraints(
                            [
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                                Constraint::Percentage(25),
                            ]
                            .as_ref(),
                        )
                        .split(chunks[0]);

                    f.render_widget(
                        Paragraph::new(format!("Pinging {}", &args.host)),
                        header_layout[0],
                    );

                    let stats = app.stats();

                    f.render_widget(
                        Paragraph::new(format!(
                            "min {:?}",
                            Duration::from_micros(stats.minimum().unwrap_or(0))
                        )),
                        header_layout[1],
                    );
                    f.render_widget(
                        Paragraph::new(format!(
                            "max {:?}",
                            Duration::from_micros(stats.maximum().unwrap_or(0))
                        )),
                        header_layout[2],
                    );
                    f.render_widget(
                        Paragraph::new(format!(
                            "p95 {:?}",
                            Duration::from_micros(stats.percentile(95.0).unwrap_or(0))
                        )),
                        header_layout[3],
                    );

                    let dataset = Dataset::default()
                        .marker(symbols::Marker::Braille)
                        .style(Style::default().fg(Color::Cyan))
                        .graph_type(GraphType::Line)
                        .data(&app.data);

                    let y_axis_bounds = app.y_axis_bounds();

                    let chart = Chart::new(vec![dataset])
                        .block(Block::default().borders(Borders::NONE))
                        .x_axis(
                            Axis::default()
                                .style(Style::default().fg(Color::Gray))
                                .bounds(app.window),
                        )
                        .y_axis(
                            Axis::default()
                                .title("Time")
                                .style(Style::default().fg(Color::Gray))
                                .bounds(y_axis_bounds)
                                .labels(app.y_axis_labels(y_axis_bounds)),
                        );
                    f.render_widget(chart, chunks[1]);
                })?;
            }
            Event::Input(input) => match input.code {
                KeyCode::Char('q') | KeyCode::Esc => {
                    break;
                }
                KeyCode::Char('c') if input.modifiers == KeyModifiers::CONTROL => {
                    break;
                }
                _ => {}
            },
        }
    }

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

    Ok(())
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;

pub mod lattice;
mod config;
mod output;
mod substrates;

use std::io::prelude::*;

fn main() {
    let mut stderr = std::io::stderr();

    let matches = clap_app!(create_system =>
        (version: crate_version!())
        (author: crate_authors!())
        (about: crate_description!())
        (@arg output: <PATH> "output .gro file (the extension will be corrected)")
        (@arg x: <X> "size along x")
        (@arg y: <Y> "size along y")
        (@arg title: -t --title [STR] +takes_value "title of system")
    ).get_matches();

    let config = config::Config::new(matches).unwrap_or_else(|err| {
        writeln!(&mut stderr, "{}", err).expect("could not write to stderr");
        std::process::exit(1)
    });

    if let Err(e) = config::run(config) {
        writeln!(&mut stderr, "{}", e).expect("could not write to stderr");
        std::process::exit(1)
    }
}
<commit_msg>Add usage documentation<commit_after>//! Construct substrates for use in molecular dynamics simulations.
//! Writes to GROMOS formatted files.
//!
//! # Usage
//! ```bash
//! USAGE:
//!     create_system [OPTIONS] <PATH> <X> <Y>
//!
//! FLAGS:
//!     -h, --help       Prints help information
//!     -V, --version    Prints version information
//!
//! OPTIONS:
//!     -t, --title <STR>    title of system
//!
//! ARGS:
//!     <PATH>    output .gro file (the extension will be corrected)
//!     <X>       size along x
//!     <Y>       size along y
//! ```
//!
//! # Available Substrates
//! Spacings and translations for all substrates is currently hard-coded.
//! Preferably this should be set in some configuration files or as an option
//! input by the user during runtime.
//!
//! ## Graphene
//! A monolayer of carbon atoms set in a hexagonal honeycomb structure.
//! The spacing between every atom is 0.142 nm.
//!
//! ## Silica
//! A monolayer of rigid SiO2 molecules set in a triclinic formation with
//! with spacing 0.450 nm along both base vectors and an angle of 60 degrees
//! between them.

#[macro_use]
extern crate clap;

mod lattice;
mod config;
mod output;
mod substrates;

use std::io::prelude::*;

fn main() {
    let mut stderr = std::io::stderr();

    let matches = clap_app!(create_system =>
        (version: crate_version!())
        (author: crate_authors!())
        (about: crate_description!())
        (@arg output: <PATH> "output .gro file (the extension will be corrected)")
        (@arg x: <X> "size along x")
        (@arg y: <Y> "size along y")
        (@arg title: -t --title [STR] +takes_value "title of system")
    ).get_matches();

    let config = config::Config::new(matches).unwrap_or_else(|err| {
        writeln!(&mut stderr, "{}", err).expect("could not write to stderr");
        std::process::exit(1)
    });

    if let Err(e) = config::run(config) {
        writeln!(&mut stderr, "{}", e).expect("could not write to stderr");
        std::process::exit(1)
    }
}
<|endoftext|>


<commit_before>//! [rust-kr.org] [![Docker Badge]][Docker Hub]
//! ========
//! 한국 러스트 사용자 그룹 홈페이지의 소스코드 입니다.
//!
//! ```bash
//! docker run --detach \
//!   --name rust-kr.org \
//!   --restart always \
//!   --publish 127.0.0.1:8000:8000 \
//!   simnalamburt/rust-kr.org
//!   snu.jokbo.me
//! ```
//!
//! [rust-kr.org]: https://rust-kr.org
//! [Docker Badge]: https://badgen.net/docker/pulls/simnalamburt/rust-kr.org?icon=docker&label=pulls
//! [Docker Hub]: https://hub.docker.com/r/simnalamburt/rust-kr.org/

// rust-kr.org
// Copyright (C) 2016-2018  Hyeon Kim
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::collections::BTreeMap;
use iron::{prelude::*, status, itry};
use iron::middleware::{Handler, AfterMiddleware};
use staticfile::Static;
use handlebars_iron::{Template, HandlebarsEngine, DirectorySource};
use router::router;

//
// Configs
//
/// 서버가 소켓을 열 주소
const ADDR: &'static str = "[::]:8000";
/// 마크다운 문서가 담긴 주소
const DOCS_DIR: &'static str = "docs";

/// Entry point
fn main() {
    //
    //      User sends request
    //               ⇩
    //                                '/public' 디렉토리에서 해당 파일이 있는지 먼저 찾아본다.
    //    try static file serving     있을경우 이를 전송하고, 없을경우 아래의 404 page handler로
    //                                넘어간다.
    //               ⇩
    //        "main handlers"         index, page, all_docs 등의 함수로 요청을 처리한다
    //               ⇩
    //       404 page handler         위의 두개가 모두 실패하였을경우 404페이지를 렌더링한다
    //               ⇩
    //     handlebar templating
    //

    let mut c = Chain::new(router! {
        index: get "/" => index,
        page: get "/pages/:name" => page,
        all_docs: get "/pages/_pages" => all_docs,
    });

    // Static file serving
    c.link_around(move |main: Box<dyn Handler>| -> Box<dyn Handler> {
        Box::new(move |req: &mut Request| -> IronResult<Response> {
            Static::new("public/").handle(req)
                .or_else(|_| main.handle(req))
        })
    });
    // 404 page handler
    c.link_after(catch(|mut err: IronError| {
        if err.response.body.is_some() { return Err(err); }
        if err.response.status != Some(status::NotFound) { return Err(err); }

        // TODO: 좀더 내용 채우기
        let mut data = BTreeMap::new();
        data.insert("content", r#"

404

This is not the web page you are looking for.

        "#);
        err.response.set_mut(Template::new("default", data));
        Err(err)
    }));
    // Handlebar templating
    c.link_after({
        let mut hbr = HandlebarsEngine::new();
        hbr.add(Box::new(DirectorySource::new("templates", ".hbs")));
        if let Err(r) = hbr.reload() {
            use std::error::Error;

            panic!("{}", r.description());
        }
        hbr
    });

    println!("\nServer running at \x1b[32mhttp://{}\x1b[0m\n", ADDR);
    Iron::new(c).http(ADDR).unwrap();
}

/// Root page handler
fn index(_: &mut Request) -> IronResult<Response> {
    read_docs("README")
}

/// Wiki page handler
fn page(req: &mut Request) -> IronResult<Response> {
    use router::Router;

    // Parse URL
    let name = req.extensions.get::<Router>().unwrap().find("name").unwrap_or("README");
    read_docs(name)
}

/// "All documents" page handler
fn all_docs(_: &mut Request) -> IronResult<Response> {
    use std::fs::read_dir;

    let mut paths: Vec<_> = itry!(read_dir(DOCS_DIR), status::NotFound)
        .filter_map(|f| f.ok())
        .map(|f| f.path())
        .filter(|p| p.is_file())
        .filter(|p| p.as_os_str().to_str()
            .map(|s| s.ends_with(".md"))
            .unwrap_or(false))
        .collect();
    paths.sort();

    let pages = paths.iter()
        .filter_map(|p| p.file_stem())
        .filter_map(|stem| stem.to_str());

    // TODO: 러스트로 이짓하지 말고 Handlebar로 대체
    let mut html = "
".to_string();
    for page in pages {
        use std::fmt::Write;

        write!(&mut html, r#"
{0}
"#, page).unwrap();
    }
    html.push_str("
");

    // Fill in to the template
    let mut data = BTreeMap::new();
    data.insert("content", html);
    Ok(Response::with((status::Ok, Template::new("default", data))))
}

/// `name`을 인자로 받아, `docs/<name>.md` 파일을 렌더링하여 `IronResult<Response>`로 반환합니다.
fn read_docs(name: &str) -> IronResult<Response> {
    use std::{fs::File, io::Read};
    use pulldown_cmark::{Parser, html::push_html};

    // Read file
    let path = format!("{}/{}.md", DOCS_DIR, name);
    let mut file = itry!(File::open(&path), status::NotFound);
    let mut md = String::new();
    itry!(file.read_to_string(&mut md), status::ServiceUnavailable);

    // Parse markdown
    let parser = Parser::new(&md);
    let mut html = String::new();
    push_html(&mut html, parser);

    // Fill in to the template
    let mut data = BTreeMap::new();
    data.insert("content", html);
    Ok(Response::with((status::Ok, Template::new("default", data))))
}

/// Helper struct for handle 404 page of rust-kr
struct RustkrHandler<F> { handler: F }
/// Helper function for handle 404 page of rust-kr
fn catch<F>(handler: F) -> RustkrHandler<F> { RustkrHandler { handler: handler } }
impl<F> AfterMiddleware for RustkrHandler<F>
    where F: Send + Sync + 'static + Fn(IronError) -> IronResult<Response> {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        (self.handler)(err)
    }
}
<commit_msg>Misc update to main.rs<commit_after>//! [rust-kr.org] [![Docker Badge]][Docker Hub]
//! ========
//! 한국 러스트 사용자 그룹 홈페이지의 소스코드 입니다.
//!
//! ```bash
//! docker run --detach \
//!   --name rust-kr.org \
//!   --restart always \
//!   --publish 127.0.0.1:8000:8000 \
//!   simnalamburt/rust-kr.org
//!   snu.jokbo.me
//! ```
//!
//! [rust-kr.org]: https://rust-kr.org
//! [Docker Badge]: https://badgen.net/docker/pulls/simnalamburt/rust-kr.org?icon=docker&label=pulls
//! [Docker Hub]: https://hub.docker.com/r/simnalamburt/rust-kr.org/

// rust-kr.org
// Copyright (C) 2016-2018  Hyeon Kim
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero General Public License as published
// by the Free Software Foundation, either version 3 of the License, or
// (at your option) any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program.  If not, see <http://www.gnu.org/licenses/>.

use std::collections::BTreeMap;
use iron::{prelude::*, status, itry};
use iron::middleware::{Handler, AfterMiddleware};
use staticfile::Static;
use handlebars_iron::{Template, HandlebarsEngine, DirectorySource};
use router::router;

//
// Configs
//
/// 서버가 소켓을 열 주소
const ADDR: &str = "[::]:8000";
/// 마크다운 문서가 담긴 주소
const DOCS_DIR: &str = "docs";

/// Entry point
fn main() {
    //
    //      User sends request
    //               ⇩
    //                                '/public' 디렉토리에서 해당 파일이 있는지 먼저 찾아본다.
    //    try static file serving     있을경우 이를 전송하고, 없을경우 아래의 404 page handler로
    //                                넘어간다.
    //               ⇩
    //        "main handlers"         index, page, all_docs 등의 함수로 요청을 처리한다
    //               ⇩
    //       404 page handler         위의 두개가 모두 실패하였을경우 404페이지를 렌더링한다
    //               ⇩
    //     handlebar templating
    //

    let mut c = Chain::new(router! {
        index: get "/" => index,
        page: get "/pages/:name" => page,
        all_docs: get "/pages/_pages" => all_docs,
    });

    // Static file serving
    c.link_around(|main: Box<dyn Handler>| -> Box<dyn Handler> {
        Box::new(move |req: &mut Request| -> IronResult<Response> {
            Static::new("public/").handle(req)
                .or_else(|_| main.handle(req))
        })
    });
    // 404 page handler
    c.link_after(catch(|mut err: IronError| {
        if err.response.body.is_some() { return Err(err); }
        if err.response.status != Some(status::NotFound) { return Err(err); }

        // TODO: 좀더 내용 채우기
        let mut data = BTreeMap::new();
        data.insert("content", r#"

404

This is not the web page you are looking for.

        "#);
        err.response.set_mut(Template::new("default", data));
        Err(err)
    }));
    // Handlebar templating
    c.link_after({
        let mut hbr = HandlebarsEngine::new();
        hbr.add(Box::new(DirectorySource::new("templates", ".hbs")));
        if let Err(r) = hbr.reload() {
            use std::error::Error;

            panic!("{}", r.description());
        }
        hbr
    });

    println!("\nServer running at \x1b[32mhttp://{}\x1b[0m\n", ADDR);
    Iron::new(c).http(ADDR).unwrap();
}

/// Root page handler
fn index(_: &mut Request) -> IronResult<Response> {
    read_docs("README")
}

/// Wiki page handler
fn page(req: &mut Request) -> IronResult<Response> {
    use router::Router;

    // Parse URL
    let name = req.extensions.get::<Router>().unwrap().find("name").unwrap_or("README");
    read_docs(name)
}

/// "All documents" page handler
fn all_docs(_: &mut Request) -> IronResult<Response> {
    use std::fs::read_dir;

    let mut paths: Vec<_> = itry!(read_dir(DOCS_DIR), status::NotFound)
        .filter_map(|f| f.ok())
        .map(|f| f.path())
        .filter(|p| p.is_file())
        .filter(|p| p.as_os_str().to_str()
            .map(|s| s.ends_with(".md"))
            .unwrap_or(false))
        .collect();
    paths.sort();

    let pages = paths.iter()
        .filter_map(|p| p.file_stem())
        .filter_map(|stem| stem.to_str());

    // TODO: 러스트로 이짓하지 말고 Handlebar로 대체
    let mut html = "
".to_string();
    for page in pages {
        use std::fmt::Write;

        write!(&mut html, r#"
{0}
"#, page).unwrap();
    }
    html.push_str("
");

    // Fill in to the template
    let mut data = BTreeMap::new();
    data.insert("content", html);
    Ok(Response::with((status::Ok, Template::new("default", data))))
}

/// `name`을 인자로 받아, `docs/<name>.md` 파일을 렌더링하여 `IronResult<Response>`로 반환합니다.
fn read_docs(name: &str) -> IronResult<Response> {
    use std::{fs::File, io::Read};
    use pulldown_cmark::{Parser, html::push_html};

    // Read file
    let path = format!("{}/{}.md", DOCS_DIR, name);
    let mut file = itry!(File::open(&path), status::NotFound);
    let mut md = String::new();
    itry!(file.read_to_string(&mut md), status::ServiceUnavailable);

    // Parse markdown
    let parser = Parser::new(&md);
    let mut html = String::new();
    push_html(&mut html, parser);

    // Fill in to the template
    let mut data = BTreeMap::new();
    data.insert("content", html);
    Ok(Response::with((status::Ok, Template::new("default", data))))
}

/// Helper struct for handle 404 page of rust-kr
struct RustkrHandler<F> { handler: F }
/// Helper function for handle 404 page of rust-kr
fn catch<F>(handler: F) -> RustkrHandler<F> { RustkrHandler { handler: handler } }
impl<F> AfterMiddleware for RustkrHandler<F>
    where F: Send + Sync + 'static + Fn(IronError) -> IronResult<Response> {
    fn catch(&self, _: &mut Request, err: IronError) -> IronResult<Response> {
        (self.handler)(err)
    }
}
<|endoftext|>