<commit_before>extern crate tempfile;
extern crate rustyline;

mod lexer;
mod parser;
mod evaluator;
mod error;
mod constants;
mod function;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use tempfile::NamedTempFile;
use constants::VERSION;
use lexer::lexer;
use parser::Parser;
use evaluator::Evaluator;
use error::{LexerError, ParserError, EvaluatorError};

fn prelude() {
    println!("bspl {}", VERSION);
    println!("Bit-Shift-Print Loop");
    println!("Type 'help', 'license', or 'version' for more information.");
}

fn error_message(width: usize, msg: &str) {
    println!("{caret:>width$}\n.. {}", msg, caret = "^", width = width);
}

fn display_results(results: Vec<String>) {
    let mut result_iter = results.iter();
    while let Some(result) = result_iter.next() {
        println!(".. {}", result);
    }

    if let Some(final_result) = results.last() {
        let parsed_final_result = final_result.parse::<u32>();
        if parsed_final_result.is_ok() {
            let parsed_final_result = parsed_final_result.unwrap();
            println!("D: {}", parsed_final_result);
            println!("H: {:#x}", parsed_final_result);
            println!("B: {:#b}", parsed_final_result);
        }
    }
}

fn repl() {
    let mut repl = Editor::<()>::new();
    let prompt = "=> ";
    let prompt_len = prompt.len() + 1;
    let tmp_file = NamedTempFile::new().unwrap();
    let _ = repl.load_history(tmp_file.path());

    let mut parser = Parser::default();
    let evaluator = Evaluator::default();

    loop {
        match repl.readline(prompt) {
            Ok(line) => {
                repl.add_history_entry(&line);
                match lexer(&line) {
                    Ok(tokens) => {
                        match parser.parse(tokens) {
                            Ok(parsed_tokens) => {
                                match evaluator.evaluate(parsed_tokens) {
                                    Ok(result) => {
                                        display_results(result);
                                    }
                                    Err(EvaluatorError::MissingArgument(position)) => {
                                        error_message(position + prompt_len,
                                                      "Missing argument from expression");
                                    }
                                    Err(EvaluatorError::TooManyArguments) => {
                                        error_message(prompt_len,
                                                      "Too many arguments in expression");
                                    }
                                    Err(EvaluatorError::OverflowShift(position)) => {
                                        error_message(position + prompt_len,
                                                      "Shift overflow error");
                                    }
                                    Err(EvaluatorError::UnknownKeyword(position)) => {
                                        error_message(position + prompt_len, "Not a valid keyword");
                                    }
                                    Err(EvaluatorError::Exit) => break,
                                }
                            }
                            Err(ParserError::MissingOpeningBracket(position)) => {
                                error_message(position + prompt_len, "Missing an opening bracket");
                            }
                            Err(ParserError::MissingClosingBracket(position)) => {
                                error_message(position + prompt_len, "Missing a closing bracket");
                            }
                            Err(ParserError::KeywordError(position)) => {
                                error_message(position + prompt_len,
                                              "Cannot use keyword in expression");
                            }
                        }
                    }
                    Err(LexerError::RadixError(position)) => {
                        error_message(position + prompt_len,
                                      "Not a recognizable decimal, hexadecimal, or keyword");
                    }
                    Err(LexerError::UnknownOperator(position)) => {
                        error_message(position + prompt_len, "Not a valid operator");
                    }
                }
            }
            Err(ReadlineError::Eof) => break,
            Err(ReadlineError::Interrupted) => {
                println!("Type 'exit' or press Ctrl-D to leave bspl");
                continue;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }

        }
        repl.save_history(tmp_file.path()).unwrap();
    }
}

pub fn main() {
    prelude();
    repl();
}
<commit_msg>Replace 'recognizable' with 'valid'<commit_after>extern crate tempfile;
extern crate rustyline;

mod lexer;
mod parser;
mod evaluator;
mod error;
mod constants;
mod function;

use rustyline::error::ReadlineError;
use rustyline::Editor;
use tempfile::NamedTempFile;
use constants::VERSION;
use lexer::lexer;
use parser::Parser;
use evaluator::Evaluator;
use error::{LexerError, ParserError, EvaluatorError};

fn prelude() {
    println!("bspl {}", VERSION);
    println!("Bit-Shift-Print Loop");
    println!("Type 'help', 'license', or 'version' for more information.");
}

fn error_message(width: usize, msg: &str) {
    println!("{caret:>width$}\n.. {}", msg, caret = "^", width = width);
}

fn display_results(results: Vec<String>) {
    let mut result_iter = results.iter();
    while let Some(result) = result_iter.next() {
        println!(".. {}", result);
    }

    if let Some(final_result) = results.last() {
        let parsed_final_result = final_result.parse::<u32>();
        if parsed_final_result.is_ok() {
            let parsed_final_result = parsed_final_result.unwrap();
            println!("D: {}", parsed_final_result);
            println!("H: {:#x}", parsed_final_result);
            println!("B: {:#b}", parsed_final_result);
        }
    }
}

fn repl() {
    let mut repl = Editor::<()>::new();
    let prompt = "=> ";
    let prompt_len = prompt.len() + 1;
    let tmp_file = NamedTempFile::new().unwrap();
    let _ = repl.load_history(tmp_file.path());

    let mut parser = Parser::default();
    let evaluator = Evaluator::default();

    loop {
        match repl.readline(prompt) {
            Ok(line) => {
                repl.add_history_entry(&line);
                match lexer(&line) {
                    Ok(tokens) => {
                        match parser.parse(tokens) {
                            Ok(parsed_tokens) => {
                                match evaluator.evaluate(parsed_tokens) {
                                    Ok(result) => {
                                        display_results(result);
                                    }
                                    Err(EvaluatorError::MissingArgument(position)) => {
                                        error_message(position + prompt_len,
                                                      "Missing argument from expression");
                                    }
                                    Err(EvaluatorError::TooManyArguments) => {
                                        error_message(prompt_len,
                                                      "Too many arguments in expression");
                                    }
                                    Err(EvaluatorError::OverflowShift(position)) => {
                                        error_message(position + prompt_len,
                                                      "Shift overflow error");
                                    }
                                    Err(EvaluatorError::UnknownKeyword(position)) => {
                                        error_message(position + prompt_len, "Not a valid keyword");
                                    }
                                    Err(EvaluatorError::Exit) => break,
                                }
                            }
                            Err(ParserError::MissingOpeningBracket(position)) => {
                                error_message(position + prompt_len, "Missing an opening bracket");
                            }
                            Err(ParserError::MissingClosingBracket(position)) => {
                                error_message(position + prompt_len, "Missing a closing bracket");
                            }
                            Err(ParserError::KeywordError(position)) => {
                                error_message(position + prompt_len,
                                              "Cannot use keyword in expression");
                            }
                        }
                    }
                    Err(LexerError::RadixError(position)) => {
                        error_message(position + prompt_len,
                                      "Not a valid decimal, hexadecimal, or keyword");
                    }
                    Err(LexerError::UnknownOperator(position)) => {
                        error_message(position + prompt_len, "Not a valid operator");
                    }
                }
            }
            Err(ReadlineError::Eof) => break,
            Err(ReadlineError::Interrupted) => {
                println!("Type 'exit' or press Ctrl-D to leave bspl");
                continue;
            }
            Err(err) => {
                println!("Error: {:?}", err);
                break;
            }

        }
        repl.save_history(tmp_file.path()).unwrap();
    }
}

pub fn main() {
    prelude();
    repl();
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate clap;
extern crate ansi_term;
extern crate atty;
extern crate regex;
extern crate regex_syntax;
extern crate ignore;
extern crate num_cpus;

pub mod lscolors;
pub mod fshelper;
mod app;
mod internal;
mod output;
mod walk;

use std::env;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time;

use atty::Stream;
use regex::RegexBuilder;

use internal::{error, pattern_has_uppercase_char, FdOptions, PathDisplay, ROOT_DIR};
use lscolors::LsColors;
use walk::FileType;

fn main() {
    let matches = app::build_app().get_matches();

    // Get the search pattern
    let empty_pattern = String::new();
    let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern);

    // Get the current working directory
    let current_dir = Path::new(".");
    if !current_dir.is_dir() {
        error("Error: could not get current directory.");
    }

    // Get the root directory for the search
    let mut root_dir_buf = match matches.value_of("path") {
        Some(path) => PathBuf::from(path),
        None => current_dir.to_path_buf(),
    };
    if !root_dir_buf.is_dir() {
        error(&format!(
            "Error: '{}' is not a directory.",
            root_dir_buf.to_string_lossy()
        ));
    }

    let path_display = if matches.is_present("absolute-path") || root_dir_buf.is_absolute() {
        PathDisplay::Absolute
    } else {
        PathDisplay::Relative
    };

    if path_display == PathDisplay::Absolute && root_dir_buf.is_relative() {
        root_dir_buf = fshelper::absolute_path(root_dir_buf.as_path()).unwrap();
    }
    let root_dir = root_dir_buf.as_path();

    // The search will be case-sensitive if the command line flag is set or
    // if the pattern has an uppercase character (smart case).
    let case_sensitive = !matches.is_present("ignore-case") &&
        (matches.is_present("case-sensitive") || pattern_has_uppercase_char(pattern));

    let colored_output = match matches.value_of("color") {
        Some("always") => true,
        Some("never") => false,
        _ => atty::is(Stream::Stdout),
    };

    let ls_colors = if colored_output {
        Some(
            env::var("LS_COLORS")
                .ok()
                .map(|val| LsColors::from_string(&val))
                .unwrap_or_default(),
        )
    } else {
        None
    };

    let config = FdOptions {
        case_sensitive: case_sensitive,
        search_full_path: matches.is_present("full-path"),
        ignore_hidden: !(matches.is_present("hidden") ||
                             matches.occurrences_of("rg-alias-hidden-ignore") >= 2),
        read_ignore: !(matches.is_present("no-ignore") ||
                           matches.is_present("rg-alias-hidden-ignore")),
        follow_links: matches.is_present("follow"),
        null_separator: matches.is_present("null_separator"),
        max_depth: matches.value_of("depth").and_then(|n| {
            usize::from_str_radix(n, 10).ok()
        }),
        threads: std::cmp::max(
            matches
                .value_of("threads")
                .and_then(|n| usize::from_str_radix(n, 10).ok())
                .unwrap_or_else(num_cpus::get),
            1,
        ),
        max_buffer_time: matches
            .value_of("max-buffer-time")
            .and_then(|n| u64::from_str_radix(n, 10).ok())
            .map(time::Duration::from_millis),
        path_display: path_display,
        ls_colors: ls_colors,
        file_type: match matches.value_of("file-type") {
            Some("f") | Some("file") => FileType::RegularFile,
            Some("d") |
            Some("directory") => FileType::Directory,
            Some("l") | Some("symlink") => FileType::SymLink,
            _ => FileType::Any,
        },
        extension: matches.value_of("extension").map(|e| {
            e.trim_left_matches('.').to_lowercase()
        }),
    };

    // If base_dir is ROOT_DIR, then root_dir must be absolute.
    // Otherwise root_dir/entry cannot be turned into an existing relative path from base_dir.
    //
    // We utilize ROOT_DIR to avoid resolving parent directories the root_dir.
    let base_dir_buf = match config.path_display {
        PathDisplay::Relative => current_dir.to_path_buf(),
        PathDisplay::Absolute => PathBuf::from(ROOT_DIR),
    };
    let base_dir = base_dir_buf.as_path();

    match RegexBuilder::new(pattern)
        .case_insensitive(!config.case_sensitive)
        .dot_matches_new_line(true)
        .build() {
        Ok(re) => walk::scan(root_dir, Arc::new(re), base_dir, Arc::new(config)),
        Err(err) => error(err.description()),
    }
}
<commit_msg>Use .canonicalize() to check paths & Reword comments<commit_after>#[macro_use]
extern crate clap;
extern crate ansi_term;
extern crate atty;
extern crate regex;
extern crate regex_syntax;
extern crate ignore;
extern crate num_cpus;

pub mod lscolors;
pub mod fshelper;
mod app;
mod internal;
mod output;
mod walk;

use std::env;
use std::error::Error;
use std::path::{Path, PathBuf};
use std::sync::Arc;
use std::time;

use atty::Stream;
use regex::RegexBuilder;

use internal::{error, pattern_has_uppercase_char, FdOptions, PathDisplay, ROOT_DIR};
use lscolors::LsColors;
use walk::FileType;

fn main() {
    let matches = app::build_app().get_matches();

    // Get the search pattern
    let empty_pattern = String::new();
    let pattern = matches.value_of("pattern").unwrap_or(&empty_pattern);

    // Get the current working directory
    let current_dir = Path::new(".");
    // .is_dir() is not guarandteed to be intuitively correct for "." and ".."
    if let Err(_) = current_dir.canonicalize() {
        error("Error: could not get current directory.");
    }

    // Get the root directory for the search
    let mut root_dir_buf = match matches.value_of("path") {
        Some(path) => PathBuf::from(path),
        None => current_dir.to_path_buf(),
    };
    if let Err(_) = root_dir_buf.canonicalize() {
        error(&format!(
            "Error: '{}' is not a directory.",
            root_dir_buf.to_string_lossy()
        ));
    }

    let path_display = if matches.is_present("absolute-path") || root_dir_buf.is_absolute() {
        PathDisplay::Absolute
    } else {
        PathDisplay::Relative
    };

    if path_display == PathDisplay::Absolute && root_dir_buf.is_relative() {
        root_dir_buf = fshelper::absolute_path(root_dir_buf.as_path()).unwrap();
    }
    let root_dir = root_dir_buf.as_path();

    // The search will be case-sensitive if the command line flag is set or
    // if the pattern has an uppercase character (smart case).
    let case_sensitive = !matches.is_present("ignore-case") &&
        (matches.is_present("case-sensitive") || pattern_has_uppercase_char(pattern));

    let colored_output = match matches.value_of("color") {
        Some("always") => true,
        Some("never") => false,
        _ => atty::is(Stream::Stdout),
    };

    let ls_colors = if colored_output {
        Some(
            env::var("LS_COLORS")
                .ok()
                .map(|val| LsColors::from_string(&val))
                .unwrap_or_default(),
        )
    } else {
        None
    };

    let config = FdOptions {
        case_sensitive: case_sensitive,
        search_full_path: matches.is_present("full-path"),
        ignore_hidden: !(matches.is_present("hidden") ||
                             matches.occurrences_of("rg-alias-hidden-ignore") >= 2),
        read_ignore: !(matches.is_present("no-ignore") ||
                           matches.is_present("rg-alias-hidden-ignore")),
        follow_links: matches.is_present("follow"),
        null_separator: matches.is_present("null_separator"),
        max_depth: matches.value_of("depth").and_then(|n| {
            usize::from_str_radix(n, 10).ok()
        }),
        threads: std::cmp::max(
            matches
                .value_of("threads")
                .and_then(|n| usize::from_str_radix(n, 10).ok())
                .unwrap_or_else(num_cpus::get),
            1,
        ),
        max_buffer_time: matches
            .value_of("max-buffer-time")
            .and_then(|n| u64::from_str_radix(n, 10).ok())
            .map(time::Duration::from_millis),
        path_display: path_display,
        ls_colors: ls_colors,
        file_type: match matches.value_of("file-type") {
            Some("f") | Some("file") => FileType::RegularFile,
            Some("d") |
            Some("directory") => FileType::Directory,
            Some("l") | Some("symlink") => FileType::SymLink,
            _ => FileType::Any,
        },
        extension: matches.value_of("extension").map(|e| {
            e.trim_left_matches('.').to_lowercase()
        }),
    };

    // If base_dir is ROOT_DIR, then root_dir must be absolute.
    // Otherwise root_dir/entry cannot be turned into an existing relative path from base_dir.
    //
    // We utilize ROOT_DIR to avoid resolving the components of root_dir.
    let base_dir_buf = match config.path_display {
        PathDisplay::Relative => current_dir.to_path_buf(),
        PathDisplay::Absolute => PathBuf::from(ROOT_DIR),
    };
    let base_dir = base_dir_buf.as_path();

    match RegexBuilder::new(pattern)
        .case_insensitive(!config.case_sensitive)
        .dot_matches_new_line(true)
        .build() {
        Ok(re) => walk::scan(root_dir, Arc::new(re), base_dir, Arc::new(config)),
        Err(err) => error(err.description()),
    }
}
<|endoftext|>


<commit_before>#![cfg_attr(test, allow(dead_code))]
#![feature(collections, env, libc, old_io, os)]
#![cfg_attr(not(windows), feature(old_path, fs, io, std_misc))]

extern crate libc;

mod args;
mod matching;
mod screen;
#[cfg(not(windows))] mod ansi;

use std::env;
use std::old_io;
use std::cmp::min;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;

fn main() {
    let args = match args::parse_args() {
        Some(args) => args,
        None => {
            env::set_exit_status(1);
            return;
        },
    };

    if args.help { return; }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();
    if args.use_first {
        let matches = matching::compute_matches(&choices, &initial_search);
        println!("{}", matches.get(0).unwrap_or(&""));
        return;
    } else {
        event_loop(&choices, &initial_search);
    }
}

fn event_loop(choices: &[&str], initial_search: &str) {
    let mut search = Search::new(choices, initial_search.to_string());
    let mut screen = Screen::open_screen();

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(&mut screen, &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in keys.iter() {
            handle_key(&mut search, key, screen.visible_choices);
        }
    }

    screen.move_cursor_to_bottom();
    println!("{}", search.get_selection());
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace => search.backspace(),
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('c') => search.cancel(),
        Control('g') => search.cancel(),
        Control('n') => search.down(visible_choices),
        Control('p') => search.up(visible_choices),
        Enter => search.done(),
        _ => {}
    }
}

struct Search<'a> {
    choices: &'a [&'a str],
    query: String,
    matches: Vec<&'a str>,
    stale: bool,
    index: usize,
    state: SearchState,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a [&'a str], initial_search: String) -> Search<'a> {
        let matches = choices.to_vec();
        Search {
            choices: choices,
            query: initial_search,
            matches: matches,
            stale: true,
            index: 0,
            state: InProgress,
        }
    }

    fn up(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == 0 { limit } else { self.index - 1 };
    }

    fn down(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == limit { 0 } else { self.index + 1 }
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.index = 0;
        self.matches = self.choices.to_vec();
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
        self.matches = self.choices.to_vec();
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.index = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.matches = self.choices.to_vec();
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(&self.matches, &self.query);
            self.stale = false;
        }
    }

    fn get_selection(&mut self) -> String {
        if self.state == Canceled {
            "".to_string()
        } else {
            self.recompute_matches();
            self.matches.get(self.index).unwrap_or(&"").to_string()
        }
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(&format!("> {} ({}/{} choices)\n", search.query, search.matches.len(), search.choices.len()));

    print_matches(screen, &search.matches, &search.query, search.index);

    let start = screen.start_line;
    screen.move_cursor(start, 2 + search.query.len() as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &[&str], query: &str, index: usize) {
    let mut i = 1;
    for choice in matches.iter() {
        let indices = matching::visual_score(choice, query);
        let max_width = screen.width;
        print_match(choice, &indices, max_width, &mut |s, highlight| {
            if i == index + 1 {
                if highlight { screen.write_red_inverted(s); } else { screen.write_inverted(s); }
            } else {
                if highlight { screen.write_red(s); } else { screen.write(s); }
            }
        });
        if i >= screen.visible_choices as usize {
            return;
        } else {
            screen.write("\n");
        }
        i += 1;
    }
}

fn print_match(choice: &str, indices: &[usize], max_width: u16, writer: &mut FnMut(&str, bool)) {
    let chars_in_choice = choice.chars().collect::<Vec<char>>().len();
    let chars_to_draw = min(chars_in_choice, max_width as usize);
    let mut last_idx = 0;
    for &idx in indices {
        let idx = min(idx, chars_to_draw);
        if last_idx >= chars_to_draw {
            return;
        }
        writer(&choice.slice_chars(last_idx, idx), false);
        if idx == chars_to_draw { return }
        writer(&choice.slice_chars(idx, idx + 1), true);
        last_idx = idx + 1;
    }
    writer(&choice.slice_chars(last_idx, chars_to_draw), false);
}

fn read_choices() -> Vec<String> {
    let mut stdin = old_io::stdio::stdin();
    let mut lines = Vec::new();

    while let Ok(mut s) = stdin.read_line() {
        trim(&mut s);
        lines.push(s);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

#[test]
fn trim_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        trim(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("\n", "");
    should_become("\n\n", "");
    should_become("asdf", "asdf");
    should_become("asdf\n", "asdf");
    should_become("asdf\nasdf\n", "asdf\nasdf");
}

#[test]
fn delete_word_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        delete_last_word(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("a", "");
    should_become("asdf", "");
    should_become("asdf asdf asdf", "asdf asdf ");
    should_become("asdf asdf asdf ", "asdf asdf ");
    should_become("asdf asdf asdf  ", "asdf asdf ");
}
<commit_msg>Fix double-wrapping of long lines on Windows<commit_after>#![cfg_attr(test, allow(dead_code))]
#![feature(collections, env, libc, old_io, os)]
#![cfg_attr(not(windows), feature(old_path, fs, io, std_misc))]

extern crate libc;

mod args;
mod matching;
mod screen;
#[cfg(not(windows))] mod ansi;

use std::env;
use std::old_io;
use std::cmp::min;
use screen::Screen;
use screen::Key;
use screen::Key::*;
use self::SearchState::*;

fn main() {
    let args = match args::parse_args() {
        Some(args) => args,
        None => {
            env::set_exit_status(1);
            return;
        },
    };

    if args.help { return; }

    let choices = read_choices();
    let initial_search = args.initial_search.clone();
    let choices = choices.iter().map(|x| &x[..]).collect::<Vec<&str>>();
    if args.use_first {
        let matches = matching::compute_matches(&choices, &initial_search);
        println!("{}", matches.get(0).unwrap_or(&""));
        return;
    } else {
        event_loop(&choices, &initial_search);
    }
}

fn event_loop(choices: &[&str], initial_search: &str) {
    let mut search = Search::new(choices, initial_search.to_string());
    let mut screen = Screen::open_screen();

    loop {
        search.recompute_matches();

        match search.state {
            InProgress => draw_screen(&mut screen, &search),
            _ => break,
        }

        let keys = screen.get_buffered_keys();
        for key in keys.iter() {
            handle_key(&mut search, key, screen.visible_choices);
        }
    }

    screen.move_cursor_to_bottom();
    println!("{}", search.get_selection());
}

fn handle_key(search: &mut Search, key: &Key, visible_choices: u16) {
    match *key {
        Char(x) => search.append(x),
        Backspace => search.backspace(),
        Control('h') => search.backspace(),
        Control('w') => search.delete_word(),
        Control('u') => search.clear_query(),
        Control('c') => search.cancel(),
        Control('g') => search.cancel(),
        Control('n') => search.down(visible_choices),
        Control('p') => search.up(visible_choices),
        Enter => search.done(),
        _ => {}
    }
}

struct Search<'a> {
    choices: &'a [&'a str],
    query: String,
    matches: Vec<&'a str>,
    stale: bool,
    index: usize,
    state: SearchState,
}

#[derive(PartialEq, Eq)]
enum SearchState {
    InProgress,
    Done,
    Canceled,
}

impl<'a> Search<'a> {
    fn new(choices: &'a [&'a str], initial_search: String) -> Search<'a> {
        let matches = choices.to_vec();
        Search {
            choices: choices,
            query: initial_search,
            matches: matches,
            stale: true,
            index: 0,
            state: InProgress,
        }
    }

    fn up(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == 0 { limit } else { self.index - 1 };
    }

    fn down(&mut self, visible_choices: u16) {
        let limit = min(visible_choices as usize - 1, self.matches.len() - 1);
        self.index = if self.index == limit { 0 } else { self.index + 1 }
    }

    fn backspace(&mut self) {
        self.query.pop();
        self.stale = true;
        self.index = 0;
        self.matches = self.choices.to_vec();
    }

    fn delete_word(&mut self) {
        self.stale = true;
        delete_last_word(&mut self.query);
        self.matches = self.choices.to_vec();
    }

    fn append(&mut self, c: char) {
        self.query.push(c);
        self.stale = true;
        self.index = 0;
    }

    fn clear_query(&mut self) {
        self.query.clear();
        self.matches = self.choices.to_vec();
    }

    fn recompute_matches(&mut self) {
        if self.stale {
            self.matches = matching::compute_matches(&self.matches, &self.query);
            self.stale = false;
        }
    }

    fn get_selection(&mut self) -> String {
        if self.state == Canceled {
            "".to_string()
        } else {
            self.recompute_matches();
            self.matches.get(self.index).unwrap_or(&"").to_string()
        }
    }

    fn cancel(&mut self) {
        self.state = Canceled
    }

    fn done(&mut self) {
        self.state = Done
    }
}

fn draw_screen(screen: &mut Screen, search: &Search) {
    screen.hide_cursor();
    screen.blank_screen();
    screen.write(&format!("> {} ({}/{} choices)\n", search.query, search.matches.len(), search.choices.len()));

    print_matches(screen, &search.matches, &search.query, search.index);

    let start = screen.start_line;
    screen.move_cursor(start, 2 + search.query.len() as u16);
    screen.show_cursor();
}

fn print_matches(screen: &mut Screen, matches: &[&str], query: &str, index: usize) {
    let mut i = 1;
    for choice in matches.iter() {
        let indices = matching::visual_score(choice, query);
        let max_width = screen.width;
        print_match(choice, &indices, max_width, &mut |s, highlight| {
            if i == index + 1 {
                if highlight { screen.write_red_inverted(s); } else { screen.write_inverted(s); }
            } else {
                if highlight { screen.write_red(s); } else { screen.write(s); }
            }
        });
        if i >= screen.visible_choices as usize {
            return;
        } else {
            screen.write("\n");
        }
        i += 1;
    }
}

fn print_match(choice: &str, indices: &[usize], max_width: u16, writer: &mut FnMut(&str, bool)) {
    #[cfg(windows)] const MARGIN: u16 = 1;
    #[cfg(not(windows))] const MARGIN: u16 = 0;
    let max_width = max_width - MARGIN;
    let chars_in_choice = choice.chars().collect::<Vec<char>>().len();
    let chars_to_draw = min(chars_in_choice, max_width as usize);
    let mut last_idx = 0;
    for &idx in indices {
        let idx = min(idx, chars_to_draw);
        if last_idx >= chars_to_draw {
            return;
        }
        writer(&choice.slice_chars(last_idx, idx), false);
        if idx == chars_to_draw { return }
        writer(&choice.slice_chars(idx, idx + 1), true);
        last_idx = idx + 1;
    }
    writer(&choice.slice_chars(last_idx, chars_to_draw), false);
}

fn read_choices() -> Vec<String> {
    let mut stdin = old_io::stdio::stdin();
    let mut lines = Vec::new();

    while let Ok(mut s) = stdin.read_line() {
        trim(&mut s);
        lines.push(s);
    }

    lines
}

pub fn trim(s: &mut String) {
    while let Some(x) = s.pop() {
        if x != '\n' {
            s.push(x);
            return;
        }
    }
}

fn delete_last_word(s: &mut String) {
    let mut deleted_something = false;
    while let Some(x) = s.pop() {
        if x == ' ' {
            if deleted_something {
                s.push(x);
                return;
            }
        } else {
            deleted_something = true;
        }
    }
}

#[test]
fn trim_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        trim(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("\n", "");
    should_become("\n\n", "");
    should_become("asdf", "asdf");
    should_become("asdf\n", "asdf");
    should_become("asdf\nasdf\n", "asdf\nasdf");
}

#[test]
fn delete_word_test() {
    fn should_become(before: &str, after: &str) {
        let mut x = before.to_string();
        delete_last_word(&mut x);
        assert_eq!(after.to_string(), x);
    }
    should_become("", "");
    should_become("a", "");
    should_become("asdf", "");
    should_become("asdf asdf asdf", "asdf asdf ");
    should_become("asdf asdf asdf ", "asdf asdf ");
    should_become("asdf asdf asdf  ", "asdf asdf ");
}
<|endoftext|>


<commit_before>use bls::{PublicKey, verify_proof_of_possession};
use types::{BeaconState, Deposit, ValidatorRecord, ValidatorStatus};
use spec::ChainSpec;

/// The size of a validators deposit in GWei.
pub const DEPOSIT_GWEI: u64 = 32_000_000_000;

#[derive(Debug, PartialEq, Clone)]
pub enum ValidatorInductionError {
    InvalidShard,
    InvaidProofOfPossession,
    InvalidWithdrawalCredentials
}

pub fn process_deposit(
    state: &mut BeaconState,
    deposit: &Deposit,
    spec: &ChainSpec)
-> Result<usize, ValidatorInductionError> {
    let deposit_input = &deposit.deposit_data.deposit_input;
    let validator_index = state.validator_registry.iter()
        .position(|validator| validator.pubkey == deposit_input.pubkey);

    match validator_index {
        // replace withdrawn validator
        Some(i) => {
            if state.validator_registry[i].withdrawal_credentials == deposit_input.withdrawal_credentials {
                state.validator_balances[i] += DEPOSIT_GWEI;
                return Ok(i);
            }

            Err(ValidatorInductionError::InvalidWithdrawalCredentials)
        },
        // no withdrawn validators; push a new one on
        None => {
            let validator = ValidatorRecord {
                pubkey: deposit_input.pubkey.clone(),
                withdrawal_credentials: deposit_input.withdrawal_credentials,
                randao_commitment: deposit_input.randao_commitment,
                randao_layers: 0,
                status: ValidatorStatus::PendingActivation,
                latest_status_change_slot: state.validator_registry_latest_change_slot.clone(),
                exit_count: 0
            };

            match min_empty_validator_index(state, spec) {
                Some(i) => {
                    state.validator_registry[i] = validator;
                    state.validator_balances[i] = DEPOSIT_GWEI;
                    Ok(i)
                },
                None => {
                    state.validator_registry.push(validator);
                    state.validator_balances.push(DEPOSIT_GWEI);
                    Ok(state.validator_registry.len() - 1)
                }
            }
        }
    }
}

fn min_empty_validator_index(
    state: &BeaconState,
    spec: &ChainSpec
) -> Option<usize> {
    for i in 0..state.validator_registry.len() {
        if state.validator_balances[i] == 0
            && state.validator_registry[i].latest_status_change_slot
                + spec.zero_balance_validator_ttl <= state.slot {
            return Some(i);
        }
    }
    None
}

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

    use bls::{Keypair, Signature};
    use hashing::proof_of_possession_hash;
    use types::{Hash256, DepositData, DepositInput};

    fn get_deposit() -> Deposit {
        let kp = Keypair::random();
        let deposit_input = DepositInput {
            pubkey: kp.pk.clone(),
            withdrawal_credentials: Hash256::zero(),
            randao_commitment: Hash256::zero(),
            proof_of_possession: get_proof_of_possession(&kp)
        };
        let deposit_data = DepositData {
            deposit_input: deposit_input,
            value: 0,
            timestamp: 0
        };
        Deposit {
            merkle_branch: Vec::new(),
            merkle_tree_index: 0,
            deposit_data: deposit_data
        }
    }

    /// Generate a proof of possession for some keypair.
    fn get_proof_of_possession(kp: &Keypair) -> Signature {
        let pop_message = proof_of_possession_hash(&kp.pk.as_bytes());
        Signature::new_hashed(&pop_message, &kp.sk)
    }

    #[test]
    fn test_validator_inductor_valid_empty_validators() {
        let mut state = BeaconState::default();
        let deposit = get_deposit();
        let spec = ChainSpec::foundation();

        let result = process_deposit(&mut state, &deposit, &spec);

        assert_eq!(result.unwrap(), 0);
        //assert!(registration_equals_record(&r, &validators[0]));
        //assert_eq!(validators.len(), 1);
    }

    /*
    #[test]
    fn test_validator_inductor_status() {
        let validators = vec![];

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let _ = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let _ = inductor.induct(&r, ValidatorStatus::Active);
        let validators = inductor.to_vec();

        assert!(validators[0].status == ValidatorStatus::PendingActivation);
        assert!(validators[1].status == ValidatorStatus::Active);
        assert_eq!(validators.len(), 2);
    }

    #[test]
    fn test_validator_inductor_valid_all_active_validators() {
        let mut validators = vec![];
        for _ in 0..5 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Active;
            validators.push(v);
        }

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 5);
        //assert!(registration_equals_record(&r, &validators[5]));
        assert_eq!(validators.len(), 6);
    }

    #[test]
    fn test_validator_inductor_valid_all_second_validator_withdrawn() {
        let mut validators = vec![];
        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::Active;
        validators.push(v);
        for _ in 0..4 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Withdrawn;
            validators.push(v);
        }

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 1);
        //assert!(registration_equals_record(&r, &validators[1]));
        assert_eq!(validators.len(), 5);
    }

    #[test]
    fn test_validator_inductor_valid_all_withdrawn_validators() {
        let mut validators = vec![];
        for _ in 0..5 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Withdrawn;
            validators.push(v);
        }

        /*
         * Ensure the first validator gets the 0'th slot
         */
        let r = get_registration();
        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();
        assert_eq!(result.unwrap(), 0);
        //assert!(registration_equals_record(&r, &validators[0]));

        /*
         * Ensure the second validator gets the 1'st slot
         */
        let r_two = get_registration();
        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r_two, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();
        assert_eq!(result.unwrap(), 1);
        //assert!(registration_equals_record(&r_two, &validators[1]));
        assert_eq!(validators.len(), 5);
    }

    #[test]
    fn test_validator_inductor_shard_too_high() {
        let validators = vec![];

        let mut r = get_registration();
        r.withdrawal_shard = 1025;

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result, Err(ValidatorInductionError::InvalidShard));
        assert_eq!(validators.len(), 0);
    }

    #[test]
    fn test_validator_inductor_shard_proof_of_possession_failure() {
        let validators = vec![];

        let mut r = get_registration();
        let kp = Keypair::random();
        r.proof_of_possession = get_proof_of_possession(&kp);

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(
            result,
            Err(ValidatorInductionError::InvaidProofOfPossession)
        );
        assert_eq!(validators.len(), 0);
    }
    */
}
<commit_msg>more testing around deposit processing<commit_after>use bls::{PublicKey, verify_proof_of_possession};
use types::{BeaconState, Deposit, ValidatorRecord, ValidatorStatus};
use spec::ChainSpec;

/// The size of a validators deposit in GWei.
pub const DEPOSIT_GWEI: u64 = 32_000_000_000;

#[derive(Debug, PartialEq, Clone)]
pub enum ValidatorInductionError {
    InvalidShard,
    InvaidProofOfPossession,
    InvalidWithdrawalCredentials
}

pub fn process_deposit(
    state: &mut BeaconState,
    deposit: &Deposit,
    spec: &ChainSpec)
-> Result<usize, ValidatorInductionError> {
    let deposit_input = &deposit.deposit_data.deposit_input;
    let validator_index = state.validator_registry.iter()
        .position(|validator| validator.pubkey == deposit_input.pubkey);

    match validator_index {
        Some(i) => {
            if state.validator_registry[i].withdrawal_credentials == deposit_input.withdrawal_credentials {
                state.validator_balances[i] += DEPOSIT_GWEI;
                return Ok(i);
            }

            Err(ValidatorInductionError::InvalidWithdrawalCredentials)
        },
        None => {
            let validator = ValidatorRecord {
                pubkey: deposit_input.pubkey.clone(),
                withdrawal_credentials: deposit_input.withdrawal_credentials,
                randao_commitment: deposit_input.randao_commitment,
                randao_layers: 0,
                status: ValidatorStatus::PendingActivation,
                latest_status_change_slot: state.validator_registry_latest_change_slot.clone(),
                exit_count: 0
            };

            match min_empty_validator_index(state, spec) {
                Some(i) => {
                    state.validator_registry[i] = validator;
                    state.validator_balances[i] = DEPOSIT_GWEI;
                    Ok(i)
                },
                None => {
                    state.validator_registry.push(validator);
                    state.validator_balances.push(DEPOSIT_GWEI);
                    Ok(state.validator_registry.len() - 1)
                }
            }
        }
    }
}

fn min_empty_validator_index(
    state: &BeaconState,
    spec: &ChainSpec
) -> Option<usize> {
    for i in 0..state.validator_registry.len() {
        if state.validator_balances[i] == 0
            && state.validator_registry[i].latest_status_change_slot
                + spec.zero_balance_validator_ttl <= state.slot {
            return Some(i);
        }
    }
    None
}

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

    use bls::{Keypair, Signature};
    use hashing::proof_of_possession_hash;
    use types::{Hash256, DepositData, DepositInput};

    fn get_deposit() -> Deposit {
        let kp = Keypair::random();
        let deposit_input = DepositInput {
            pubkey: kp.pk.clone(),
            withdrawal_credentials: Hash256::zero(),
            randao_commitment: Hash256::zero(),
            proof_of_possession: get_proof_of_possession(&kp)
        };
        let deposit_data = DepositData {
            deposit_input: deposit_input,
            value: 0,
            timestamp: 0
        };
        Deposit {
            merkle_branch: Vec::new(),
            merkle_tree_index: 0,
            deposit_data: deposit_data
        }
    }

    fn deposit_equals_record(dep: &Deposit, rec: &ValidatorRecord) -> bool {
        (dep.deposit_data.deposit_input.pubkey == rec.pubkey)
            & (dep.deposit_data.deposit_input.withdrawal_credentials == rec.withdrawal_credentials)
            & (dep.deposit_data.deposit_input.randao_commitment == rec.randao_commitment)
            //& (verify_proof_of_possession(&reg.proof_of_possession, &rec.pubkey))
    }

    /// Generate a proof of possession for some keypair.
    fn get_proof_of_possession(kp: &Keypair) -> Signature {
        let pop_message = proof_of_possession_hash(&kp.pk.as_bytes());
        Signature::new_hashed(&pop_message, &kp.sk)
    }

    #[test]
    fn test_process_deposit_valid_empty_validators() {
        let mut state = BeaconState::default();
        let deposit = get_deposit();
        let spec = ChainSpec::foundation();

        let result = process_deposit(&mut state, &deposit, &spec);

        assert_eq!(result.unwrap(), 0);
        assert!(deposit_equals_record(&deposit, &state.validator_registry[0]));
        assert_eq!(state.validator_registry.len(), 1);
        assert_eq!(state.validator_balances.len(), 1);
    }

    #[test]
    fn test_process_deposit_empty_validators() {
        let mut state = BeaconState::default();
        let deposit = get_deposit();
        let spec = ChainSpec::foundation();

        let result = process_deposit(&mut state, &deposit, &spec);

        assert_eq!(result.unwrap(), 0);
        assert!(deposit_equals_record(&deposit, &state.validator_registry[0]));
        assert_eq!(state.validator_registry.len(), 1);
        assert_eq!(state.validator_balances.len(), 1);
    }

    #[test]
    fn test_process_deposits_empty_validators() {
        let mut state = BeaconState::default();
        let spec = ChainSpec::foundation();

        for i in 0..5 {
            let deposit = get_deposit();
            let result = process_deposit(&mut state, &deposit, &spec);
            assert_eq!(result.unwrap(), i);
            assert!(deposit_equals_record(&deposit, &state.validator_registry[i]));
            assert_eq!(state.validator_registry.len(), i + 1);
            assert_eq!(state.validator_balances.len(), i + 1);
        }
    }

    /*
    #[test]
    fn test_validator_inductor_status() {
        let validators = vec![];

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let _ = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let _ = inductor.induct(&r, ValidatorStatus::Active);
        let validators = inductor.to_vec();

        assert!(validators[0].status == ValidatorStatus::PendingActivation);
        assert!(validators[1].status == ValidatorStatus::Active);
        assert_eq!(validators.len(), 2);
    }

    #[test]
    fn test_validator_inductor_valid_all_active_validators() {
        let mut validators = vec![];
        for _ in 0..5 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Active;
            validators.push(v);
        }

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 5);
        //assert!(registration_equals_record(&r, &validators[5]));
        assert_eq!(validators.len(), 6);
    }

    #[test]
    fn test_validator_inductor_valid_all_second_validator_withdrawn() {
        let mut validators = vec![];
        let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
        v.status = ValidatorStatus::Active;
        validators.push(v);
        for _ in 0..4 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Withdrawn;
            validators.push(v);
        }

        let r = get_registration();

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result.unwrap(), 1);
        //assert!(registration_equals_record(&r, &validators[1]));
        assert_eq!(validators.len(), 5);
    }

    #[test]
    fn test_validator_inductor_valid_all_withdrawn_validators() {
        let mut validators = vec![];
        for _ in 0..5 {
            let (mut v, _) = ValidatorRecord::zero_with_thread_rand_keypair();
            v.status = ValidatorStatus::Withdrawn;
            validators.push(v);
        }

        /*
         * Ensure the first validator gets the 0'th slot
         */
        let r = get_registration();
        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();
        assert_eq!(result.unwrap(), 0);
        //assert!(registration_equals_record(&r, &validators[0]));

        /*
         * Ensure the second validator gets the 1'st slot
         */
        let r_two = get_registration();
        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r_two, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();
        assert_eq!(result.unwrap(), 1);
        //assert!(registration_equals_record(&r_two, &validators[1]));
        assert_eq!(validators.len(), 5);
    }

    #[test]
    fn test_validator_inductor_shard_too_high() {
        let validators = vec![];

        let mut r = get_registration();
        r.withdrawal_shard = 1025;

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(result, Err(ValidatorInductionError::InvalidShard));
        assert_eq!(validators.len(), 0);
    }

    #[test]
    fn test_validator_inductor_shard_proof_of_possession_failure() {
        let validators = vec![];

        let mut r = get_registration();
        let kp = Keypair::random();
        r.proof_of_possession = get_proof_of_possession(&kp);

        let mut inductor = ValidatorInductor::new(0, 1024, validators);
        let result = inductor.induct(&r, ValidatorStatus::PendingActivation);
        let validators = inductor.to_vec();

        assert_eq!(
            result,
            Err(ValidatorInductionError::InvaidProofOfPossession)
        );
        assert_eq!(validators.len(), 0);
    }
    */
}
<|endoftext|>


<commit_before>extern crate chrono;
extern crate nalgebra as na;
extern crate rustracer as rt;
#[macro_use]
extern crate clap;
extern crate rustc_serialize;
extern crate num_cpus;
#[macro_use(o, slog_info, slog_debug, slog_warn, slog_error, slog_trace, slog_log)]
extern crate slog;
#[macro_use]
extern crate slog_scope;
extern crate slog_stream;
extern crate thread_id;

use std::sync::Arc;
use std::num::ParseIntError;
use std::fs::OpenOptions;
use std::path::Path;
use std::io;
use std::process;

use chrono::Local;
use clap::{Arg, ArgMatches, App};
use slog::*;

use rt::bounds::Bounds2f;
use rt::bvh::BVH;
use rt::camera::Camera;
use rt::integrator::{SamplerIntegrator, Whitted, DirectLightingIntegrator, Normal,
                     AmbientOcclusion};
use rt::light::{Light, DistantLight, DiffuseAreaLight, InfiniteAreaLight};
use rt::material::matte::MatteMaterial;
use rt::material::metal::Metal;
use rt::material::plastic::Plastic;
use rt::primitive::{Primitive, GeometricPrimitive};
use rt::renderer;
use rt::scene::Scene;
use rt::shapes::disk::Disk;
use rt::shapes::sphere::Sphere;
use rt::spectrum::Spectrum;
use rt::transform;
use rt::{Transform, Point, Vector3f, Dim, Point2f};

arg_enum! {
  #[derive(Debug)]
  enum SamplerIntegratorType {
      Whitted,
      DirectLighting,
      Ao,
      Normal
  }
}

fn main() {
    // configure logger
    configure_logger();

    let matches = parse_args();
    if let Err(e) = run(matches) {
        println!("Application error: {}", e);
        process::exit(1);
    }
}

fn run(matches: ArgMatches) -> Result<(), String> {
    let parsed_dims: Result<Vec<u32>, ParseIntError> =
        matches.value_of("dim").unwrap().split('x').map(|s| s.parse::<u32>()).collect();
    let dims = parsed_dims.expect("Invalid dimensions");
    if dims.len() != 2 {
        panic!("Error: invalid dimension specification");
    }
    let dim = (dims[0], dims[1]);

    let integrator: Box<SamplerIntegrator + Send + Sync> =
        match value_t!(matches.value_of("integrator"), SamplerIntegratorType)
            .unwrap_or(SamplerIntegratorType::Whitted) {
            SamplerIntegratorType::Whitted => {
                info!("Using Whitted integrator with max ray depth of {}", 8);
                // Box::new(Whitted::new(args.flag_whitted_max_ray_depth))
                Box::new(Whitted::new(8))
            }
            SamplerIntegratorType::DirectLighting => {
                info!("Using direct lighting integrator with max ray depth of {}",
                      8);
                // Box::new(Whitted::new(args.flag_whitted_max_ray_depth))
                Box::new(DirectLightingIntegrator::new(8))
            }
            SamplerIntegratorType::Ao => {
                info!("Using Ambient Occlusion integrator with {} samples", 32);
                Box::new(AmbientOcclusion::new(32))
            }
            SamplerIntegratorType::Normal => {
                info!("Using normal facing ratio integrator");
                Box::new(Normal {})
            }
        };

    let scene = build_scene(dim, integrator);

    let start_time = std::time::Instant::now();
    let stats =
        renderer::render(scene,
                         dim,
                         matches.value_of("output").unwrap(),
                         matches.value_of("threads")
                             .and_then(|s| s.parse::<usize>().ok())
                             .unwrap_or_else(num_cpus::get),
                         matches.value_of("spp").and_then(|s| s.parse::<usize>().ok()).unwrap(),
                         32);
    // args.flag_block_size);
    let duration = start_time.elapsed();
    println!("Render time                : {}", duration.human_display());
    println!("Primary rays               : {}", stats.primary_rays);
    println!("Secondary rays             : {}", stats.secondary_rays);
    println!("Num triangles              : {}", stats.triangles);
    println!("Ray-triangle tests         : {}", stats.ray_triangle_tests);
    println!("Ray-triangle intersections : {}\t({}%)",
             stats.ray_triangle_isect,
             stats.ray_triangle_isect as f32 / stats.ray_triangle_tests as f32 * 100.0);
    println!("Fast bounding-box test     : {}", stats.fast_bbox_isect);

    Ok(())
}

fn build_scene(dim: Dim, integrator: Box<SamplerIntegrator + Send + Sync>) -> Scene {
    info!("Building scene");
    let camera = Camera::new(transform::translate_z(-5.0),
                             Point2f::new(dim.0 as f32, dim.1 as f32),
                             0.0,
                             0.0,
                             50.0);
    let mut lights: Vec<Arc<Light + Send + Sync>> = Vec::new();

    let disk = Arc::new(Disk::new(-2.0, 0.8, 0.0, 360.0, transform::rot_x(90.0)));
    let area_light =
        Arc::new(DiffuseAreaLight::new(Spectrum::rgb(3.0, 3.0, 3.0), disk.clone(), 16));
    let area_light_prim = Box::new(GeometricPrimitive {
        shape: disk.clone(),
        area_light: Some(area_light.clone()),
        material: Some(Arc::new(MatteMaterial::default())),
    });

    let bronze = Arc::new(Metal::new());
    let gold = Arc::new(Metal::gold());
    let plastic = Arc::new(Plastic::new(Spectrum::rgb(0.3, 0.3, 1.0), Spectrum::white()));
    let plastic_lines = Arc::new(Plastic::new_tex("lines.png", Spectrum::white()));
    let sphere = Box::new(GeometricPrimitive {
        shape: Arc::new(Sphere::new().radius(0.7).transform(transform::translate_y(-0.3))),
        area_light: None,
        // material: Some(Arc::new(Plastic::new(Spectrum::red(), Spectrum::white()))),
        // material: Some(Arc::new(Plastic::new_tex("lines.png", Spectrum::white()))),
        material: Some(bronze.clone()),
    });
    let bunny =
        Box::new(BVH::<GeometricPrimitive>::from_mesh_file(Path::new("models/bunny.obj"),
                                                           "bunny",
                                                           plastic.clone(),
                                                           &Transform::new(
                                                             Vector3f::new(2.0, -0.8, 0.0),
                                                             Vector3f::new(0.0, 20.0f32.to_radians(), 0.0),
                                                             0.5
                                                             ))) as Box<Primitive + Send + Sync>;
    let buddha =
        Box::new(BVH::<GeometricPrimitive>::from_mesh_file(Path::new("models/buddha.obj"),
                                                           "buddha",
                                                           gold.clone(),
                                                           &Transform::new(
                                                             Vector3f::new(-2.0, 0.0, 0.0),
                                                             Vector3f::new(0.0, 200.0f32.to_radians(), 0.0),
                                                             3.0
                                                             ))) as Box<Primitive + Send + Sync>;
    let floor = Box::new(GeometricPrimitive {
        shape: Arc::new(Disk::new(-1.0, 20.0, 0.0, 360.0, transform::rot_x(-90.0))),
        area_light: None,
        // material: Some(Arc::new(MatteMaterial::checkerboard(0.0))),
        // material: Some(Arc::new(MatteMaterial::new(Spectrum::red(), 0.0))),
        material: Some(plastic_lines.clone()),
    });

    let primitives: Vec<Box<Primitive + Sync + Send>> =
        vec![buddha, sphere, bunny, floor, area_light_prim];
    // Light
    // lights.push(area_light);
    // lights.push(Arc::new(DistantLight::new(Vector3f::new(0.0, -1.0, -5.0),
    //                                        Spectrum::rgb(0.5, 0.5, 0.5))));
    lights.push(Arc::new(InfiniteAreaLight::new(na::one(),
                                                16,
                                                Spectrum::grey(1.0),
                                                Path::new("foo"))));

    Scene::new(camera, integrator, primitives, lights)
}

fn parse_args<'a>() -> ArgMatches<'a> {
    // TODO add block-size and ao-samples and whitted-max-ray? Or maybe not since eventually it
    // will be read from the scene file...
    App::new("rustracer")
        .version("0.1")
        .author("Antoine Büsch")
        .about("Toy raytracer in Rust based on PBRTv3")
        .arg(Arg::with_name("output")
            .long("output")
            .short("o")
            .help("Output file name")
            .default_value("image.png"))
        .arg(Arg::with_name("threads")
            .long("threads")
            .short("t")
            .help("Number of worker threads"))
        .arg(Arg::with_name("dim")
            .long("dim")
            .short("d")
            .help("Dimension of the output image")
            .default_value("800x600"))
        .arg(Arg::with_name("integrator")
            .long("integrator")
            .short("i")
            .help("SamplerIntegrator to use")
            .possible_values(&SamplerIntegratorType::variants())
            .default_value("Whitted"))
        .arg(Arg::with_name("spp")
            .long("spp")
            .help("Sample per pixels")
            .default_value("4"))
        .get_matches()
}

fn configure_logger() {
    let log_path = "/tmp/rustracer.log";
    let file = OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true)
        .open(log_path)
        .unwrap();

    let drain = slog_stream::stream(file, MyFormat).fuse();
    let log = Logger::root(drain, o!());
    slog_scope::set_global_logger(log);

}

trait HumanDisplay {
    fn human_display(&self) -> String;
}
impl HumanDisplay for std::time::Duration {
    fn human_display(&self) -> String {
        let mut hours = 0;
        let mut minutes = 0;
        let mut seconds = self.as_secs();
        if seconds >= 60 {
            minutes = seconds / 60;
            seconds %= 60;
        }
        if minutes >= 60 {
            hours = minutes / 60;
            minutes %= 60;
        }
        let millis = self.subsec_nanos() / 1000000;
        format!("{}:{}:{}.{}", hours, minutes, seconds, millis)
    }
}

macro_rules! now {
    () => ( Local::now().format("%m-%d %H:%M:%S%.3f") )
}

struct MyFormat;

impl slog_stream::Format for MyFormat {
    fn format(&self,
              io: &mut io::Write,
              rinfo: &slog::Record,
              _logger_values: &slog::OwnedKeyValueList)
              -> io::Result<()> {
        let msg = format!("{} [{}][{:x}][{}:{} {}] - {}\n",
                          now!(),
                          rinfo.level(),
                          thread_id::get(),
                          rinfo.file(),
                          rinfo.line(),
                          rinfo.module(),
                          rinfo.msg());
        try!(io.write_all(msg.as_bytes()));
        Ok(())
    }
}
<commit_msg>Make -t option actually take a value...<commit_after>extern crate chrono;
extern crate nalgebra as na;
extern crate rustracer as rt;
#[macro_use]
extern crate clap;
extern crate rustc_serialize;
extern crate num_cpus;
#[macro_use(o, slog_info, slog_debug, slog_warn, slog_error, slog_trace, slog_log)]
extern crate slog;
#[macro_use]
extern crate slog_scope;
extern crate slog_stream;
extern crate thread_id;

use std::sync::Arc;
use std::num::ParseIntError;
use std::fs::OpenOptions;
use std::path::Path;
use std::io;
use std::process;

use chrono::Local;
use clap::{Arg, ArgMatches, App};
use slog::*;

use rt::bounds::Bounds2f;
use rt::bvh::BVH;
use rt::camera::Camera;
use rt::integrator::{SamplerIntegrator, Whitted, DirectLightingIntegrator, Normal,
                     AmbientOcclusion};
use rt::light::{Light, DistantLight, DiffuseAreaLight, InfiniteAreaLight};
use rt::material::matte::MatteMaterial;
use rt::material::metal::Metal;
use rt::material::plastic::Plastic;
use rt::primitive::{Primitive, GeometricPrimitive};
use rt::renderer;
use rt::scene::Scene;
use rt::shapes::disk::Disk;
use rt::shapes::sphere::Sphere;
use rt::spectrum::Spectrum;
use rt::transform;
use rt::{Transform, Point, Vector3f, Dim, Point2f};

arg_enum! {
  #[derive(Debug)]
  enum SamplerIntegratorType {
      Whitted,
      DirectLighting,
      Ao,
      Normal
  }
}

fn main() {
    // configure logger
    configure_logger();

    let matches = parse_args();
    if let Err(e) = run(matches) {
        println!("Application error: {}", e);
        process::exit(1);
    }
}

fn run(matches: ArgMatches) -> Result<(), String> {
    let parsed_dims: Result<Vec<u32>, ParseIntError> =
        matches.value_of("dim").unwrap().split('x').map(|s| s.parse::<u32>()).collect();
    let dims = parsed_dims.expect("Invalid dimensions");
    if dims.len() != 2 {
        panic!("Error: invalid dimension specification");
    }
    let dim = (dims[0], dims[1]);

    let integrator: Box<SamplerIntegrator + Send + Sync> =
        match value_t!(matches.value_of("integrator"), SamplerIntegratorType)
            .unwrap_or(SamplerIntegratorType::Whitted) {
            SamplerIntegratorType::Whitted => {
                info!("Using Whitted integrator with max ray depth of {}", 8);
                // Box::new(Whitted::new(args.flag_whitted_max_ray_depth))
                Box::new(Whitted::new(8))
            }
            SamplerIntegratorType::DirectLighting => {
                info!("Using direct lighting integrator with max ray depth of {}",
                      8);
                // Box::new(Whitted::new(args.flag_whitted_max_ray_depth))
                Box::new(DirectLightingIntegrator::new(8))
            }
            SamplerIntegratorType::Ao => {
                info!("Using Ambient Occlusion integrator with {} samples", 32);
                Box::new(AmbientOcclusion::new(32))
            }
            SamplerIntegratorType::Normal => {
                info!("Using normal facing ratio integrator");
                Box::new(Normal {})
            }
        };

    let scene = build_scene(dim, integrator);

    let start_time = std::time::Instant::now();
    let stats =
        renderer::render(scene,
                         dim,
                         matches.value_of("output").unwrap(),
                         matches.value_of("threads")
                             .and_then(|s| s.parse::<usize>().ok())
                             .unwrap_or_else(num_cpus::get),
                         matches.value_of("spp").and_then(|s| s.parse::<usize>().ok()).unwrap(),
                         32);
    // args.flag_block_size);
    let duration = start_time.elapsed();
    println!("Render time                : {}", duration.human_display());
    println!("Primary rays               : {}", stats.primary_rays);
    println!("Secondary rays             : {}", stats.secondary_rays);
    println!("Num triangles              : {}", stats.triangles);
    println!("Ray-triangle tests         : {}", stats.ray_triangle_tests);
    println!("Ray-triangle intersections : {}\t({}%)",
             stats.ray_triangle_isect,
             stats.ray_triangle_isect as f32 / stats.ray_triangle_tests as f32 * 100.0);
    println!("Fast bounding-box test     : {}", stats.fast_bbox_isect);

    Ok(())
}

fn build_scene(dim: Dim, integrator: Box<SamplerIntegrator + Send + Sync>) -> Scene {
    info!("Building scene");
    let camera = Camera::new(transform::translate_z(-5.0),
                             Point2f::new(dim.0 as f32, dim.1 as f32),
                             0.0,
                             0.0,
                             50.0);
    let mut lights: Vec<Arc<Light + Send + Sync>> = Vec::new();

    let disk = Arc::new(Disk::new(-2.0, 0.8, 0.0, 360.0, transform::rot_x(90.0)));
    let area_light =
        Arc::new(DiffuseAreaLight::new(Spectrum::rgb(3.0, 3.0, 3.0), disk.clone(), 16));
    let area_light_prim = Box::new(GeometricPrimitive {
        shape: disk.clone(),
        area_light: Some(area_light.clone()),
        material: Some(Arc::new(MatteMaterial::default())),
    });

    let bronze = Arc::new(Metal::new());
    let gold = Arc::new(Metal::gold());
    let plastic = Arc::new(Plastic::new(Spectrum::rgb(0.3, 0.3, 1.0), Spectrum::white()));
    let plastic_lines = Arc::new(Plastic::new_tex("lines.png", Spectrum::white()));
    let sphere = Box::new(GeometricPrimitive {
        shape: Arc::new(Sphere::new().radius(0.7).transform(transform::translate_y(-0.3))),
        area_light: None,
        // material: Some(Arc::new(Plastic::new(Spectrum::red(), Spectrum::white()))),
        // material: Some(Arc::new(Plastic::new_tex("lines.png", Spectrum::white()))),
        material: Some(bronze.clone()),
    });
    let bunny =
        Box::new(BVH::<GeometricPrimitive>::from_mesh_file(Path::new("models/bunny.obj"),
                                                           "bunny",
                                                           plastic.clone(),
                                                           &Transform::new(
                                                             Vector3f::new(2.0, -0.8, 0.0),
                                                             Vector3f::new(0.0, 20.0f32.to_radians(), 0.0),
                                                             0.5
                                                             ))) as Box<Primitive + Send + Sync>;
    let buddha =
        Box::new(BVH::<GeometricPrimitive>::from_mesh_file(Path::new("models/buddha.obj"),
                                                           "buddha",
                                                           gold.clone(),
                                                           &Transform::new(
                                                             Vector3f::new(-2.0, 0.0, 0.0),
                                                             Vector3f::new(0.0, 200.0f32.to_radians(), 0.0),
                                                             3.0
                                                             ))) as Box<Primitive + Send + Sync>;
    let floor = Box::new(GeometricPrimitive {
        shape: Arc::new(Disk::new(-1.0, 20.0, 0.0, 360.0, transform::rot_x(-90.0))),
        area_light: None,
        // material: Some(Arc::new(MatteMaterial::checkerboard(0.0))),
        // material: Some(Arc::new(MatteMaterial::new(Spectrum::red(), 0.0))),
        material: Some(plastic_lines.clone()),
    });

    let primitives: Vec<Box<Primitive + Sync + Send>> =
        vec![buddha, sphere, bunny, floor, area_light_prim];
    // Light
    // lights.push(area_light);
    // lights.push(Arc::new(DistantLight::new(Vector3f::new(0.0, -1.0, -5.0),
    //                                        Spectrum::rgb(0.5, 0.5, 0.5))));
    lights.push(Arc::new(InfiniteAreaLight::new(na::one(),
                                                16,
                                                Spectrum::grey(1.0),
                                                Path::new("foo"))));

    Scene::new(camera, integrator, primitives, lights)
}

fn parse_args<'a>() -> ArgMatches<'a> {
    // TODO add block-size and ao-samples and whitted-max-ray? Or maybe not since eventually it
    // will be read from the scene file...
    App::new("rustracer")
        .version("0.1")
        .author("Antoine Büsch")
        .about("Toy raytracer in Rust based on PBRTv3")
        .arg(Arg::with_name("output")
            .long("output")
            .short("o")
            .help("Output file name")
            .default_value("image.png"))
        .arg(Arg::with_name("threads")
            .long("threads")
            .short("t")
            .help("Number of worker threads")
            .takes_value(true))
        .arg(Arg::with_name("dim")
            .long("dim")
            .short("d")
            .help("Dimension of the output image")
            .default_value("800x600"))
        .arg(Arg::with_name("integrator")
            .long("integrator")
            .short("i")
            .help("SamplerIntegrator to use")
            .possible_values(&SamplerIntegratorType::variants())
            .default_value("Whitted"))
        .arg(Arg::with_name("spp")
            .long("spp")
            .help("Sample per pixels")
            .default_value("4"))
        .get_matches()
}

fn configure_logger() {
    let log_path = "/tmp/rustracer.log";
    let file = OpenOptions::new()
        .create(true)
        .write(true)
        .truncate(true)
        .open(log_path)
        .unwrap();

    let drain = slog_stream::stream(file, MyFormat).fuse();
    let log = Logger::root(drain, o!());
    slog_scope::set_global_logger(log);

}

trait HumanDisplay {
    fn human_display(&self) -> String;
}
impl HumanDisplay for std::time::Duration {
    fn human_display(&self) -> String {
        let mut hours = 0;
        let mut minutes = 0;
        let mut seconds = self.as_secs();
        if seconds >= 60 {
            minutes = seconds / 60;
            seconds %= 60;
        }
        if minutes >= 60 {
            hours = minutes / 60;
            minutes %= 60;
        }
        let millis = self.subsec_nanos() / 1000000;
        format!("{}:{}:{}.{}", hours, minutes, seconds, millis)
    }
}

macro_rules! now {
    () => ( Local::now().format("%m-%d %H:%M:%S%.3f") )
}

struct MyFormat;

impl slog_stream::Format for MyFormat {
    fn format(&self,
              io: &mut io::Write,
              rinfo: &slog::Record,
              _logger_values: &slog::OwnedKeyValueList)
              -> io::Result<()> {
        let msg = format!("{} [{}][{:x}][{}:{} {}] - {}\n",
                          now!(),
                          rinfo.level(),
                          thread_id::get(),
                          rinfo.file(),
                          rinfo.line(),
                          rinfo.module(),
                          rinfo.msg());
        try!(io.write_all(msg.as_bytes()));
        Ok(())
    }
}
<|endoftext|>


<commit_before>#[macro_use]
extern crate serenity;
extern crate rand;
extern crate typemap;

use std::env;
use std::result::Result as StdResult;
use typemap::Key;

use rand::Rng;
use rand::thread_rng;

use serenity::Result;
use serenity::client::{Client, Context};
use serenity::model;
use serenity::utils;

const PREFIX: &str = "f>";

command!(info(_ctx, msg, _args) {
    msg.channel_id.send_message(|cm| {
        cm.embed(|e| {
            e.title("lfalchbot")
             .colour(utils::Colour::blue())
             .description("(c) LFalch.com 2017")
             .footer(|f| f.text(serenity::constants::USER_AGENT))
        })
    }).unwrap();
});

command!(setgame(ctx, _msg, args) {
    ctx.set_game(model::Game::playing(&args.join(" ")));
});

command!(rpn(_ctx, msg, args) {
    match calculate(&args.join(" ")) {
        Ok(r) => msg.reply(&format!("Result: {}", r)),
        Err(e) => msg.reply(&format!("Error: {:?}", e))
    }.unwrap();
});

const CSGO_MSGS: [&str; 5] = [
    "Vi varmer op med en comp!",
    "Jeg er på!",
    "Jeg er mere til Call of Duty ..",
    "Сука блядь!",
    "Mongoskrald!"
];

const BEARTOOTH: [&str; 3] = [
    "I’m not useless! I’m just the king of excuses!",
    "One life and one decision! Make sure it ends with you still living!",
    "Lorteskat på T-shirts."
];

const RESPONSES: [&str; 4] = [
    "Undskyld, kan ikke snakke lige nu :(",
    "Hey, kan jeg ringe igen senere?",
    "Hva' så din noob!? :P",
    "Ad, hvem er du?"
];

const REDALERT: [&str; 4] = [
    "Your base is under a salt!",
    "Det gamle lortespil?",
    "Jeg er mere til CSGO.",
    "\"Mine depleted\" ..."
];

struct BotUser;

impl Key for BotUser {
    type Value = String;
}

fn main() {
    let token = env::var("DISCORD_TOKEN")
        .expect("Expected a token in the environment");
    let mut client = Client::new(&token);

    client.with_framework(|f| f
        .configure(|c| c.prefix(PREFIX))
        .command("ping", |c| c.exec_str("Pong!"))
        .on("info", info)
        .on("setgame", setgame)
        .on("rpn", rpn)
    );

    {
        let mut data = client.data.lock().unwrap();
        data.insert::<BotUser>(String::default());
    }

    client.on_ready(|ctx, ready| {
        println!("{} is connected!", ready.user.name);
        println!("Guilds:");
        for name in ready.guilds.iter().map(|g| g.id().get().unwrap().name) {
            println!("    {}", name);
        }
        {
            let mut data = ctx.data.lock().unwrap();
            *data.get_mut::<BotUser>().unwrap() = ready.user.tag();
        }
    });

    client.on_message(on_message);

    if let Err(why) = client.start() {
        println!("Client error: {:?}", why);
    }
}

fn send_random(chl: model::ChannelId, list: &[&str]) -> Result<model::Message> {
    let i = thread_rng().gen_range(0, list.len());
    chl.say(list[i])
}

fn on_message(ctx: Context, msg: model::Message) {
    if msg.author.bot {
        return
    }
    let s: String = msg.content.chars()
        .filter(|c| c.is_alphanumeric())
        .flat_map(|c|c.to_lowercase())
        .collect();

    if s.contains("css") || s.contains("source") {
        msg.channel_id.say("Hvor er mine skins!?").unwrap();
    }
    if s.contains("csgo") || s.contains("counterstrike") || s.contains("globaloffensive") {
        send_random(msg.channel_id, &CSGO_MSGS).unwrap();
    }
    if s.contains("mc") || s.contains("minecraft") {
        msg.channel_id.say("MINECRAFT!").unwrap();
    }
    if s.contains("beartooth") {
        send_random(msg.channel_id, &BEARTOOTH).unwrap();
    }
    if s.contains("rep") {
        msg.channel_id.say("Rep mig!").unwrap();
    }
    if s.contains("ftl") {
        msg.channel_id.say("Zoltan shield OP").unwrap();
    }
    if s.contains("bindingofisaac") {
        msg.channel_id.say("Mom OP").unwrap();
    }
    if s.contains("meme") {
        msg.channel_id.say("krydrede migmig'er").unwrap();
    }
    if s.contains("gunsoficarus") {
        msg.channel_id.say("Spillere online: 85").unwrap();
    }
    if s.contains("doom") {
        msg.channel_id.say("Rip and tear!").unwrap();
    }
    if s.contains("dyinglight") {
        msg.channel_id.say("Left 4 Dead?").unwrap();
    }
    if s.contains("english") {
        msg.channel_id.send_message(|cm| {
            cm.embed(|e| {
                e.image("http://dev.lfalch.com/english.jpg")
            })
        }).unwrap();
    }
    if s.contains("ra3") || s.contains("redalert") {
        send_random(msg.channel_id, &REDALERT).unwrap();
    }
    if s.contains("rusland") || s.contains("russia") || s.contains("росси") ||
        s.contains("russisk") || s.contains("russian") || s.contains("русск") || s.contains("russer"){
        msg.channel_id.say("Communism is the ultimate goal of socialism.").unwrap();
    }
    let user = {
        ctx.data.lock().unwrap().get::<BotUser>().unwrap().clone()
    };
    if msg.mentions.iter().map(|u| u.tag()).any(|u| u == user) {
        send_random(msg.channel_id, &RESPONSES).unwrap();
    }
}

#[derive(Debug, Copy, Clone)]
pub enum RpnError {
    StackTooSmall,
    UnknownOperator(char),
    NoOperands
}

pub fn calculate(calculation: &str) -> StdResult<f64, RpnError>{
    let mut stack = Vec::new();

    let operations = calculation.trim().split(' ');

    for operation in operations {
        let d = operation.parse::<f64>().ok();
        if let Some(d) = d {
            stack.push(d);
        }else{
            calc(operation.chars().next().unwrap(), &mut stack)?
        }
    }

    stack.pop().ok_or(RpnError::NoOperands)
}

fn calc(op: char, stack: &mut Vec<f64>) -> StdResult<(), RpnError>{
    match (stack.pop(), stack.pop()){
        (Some(op1), Some(op2)) => match op{
            '+' => stack.push(op2 + op1),
            '-' => stack.push(op2 - op1),
            '/' => stack.push(op2 / op1),
            '*' => stack.push(op2 * op1),
            '^' => stack.push(op2.powf(op1)),
            _ => return Err(RpnError::UnknownOperator(op))
        },
        _ => return Err(RpnError::StackTooSmall)
    }
    Ok(())
}
<commit_msg>Make and use macro for jokes<commit_after>#[macro_use]
extern crate serenity;
extern crate rand;
extern crate typemap;

use std::env;
use std::result::Result as StdResult;
use typemap::Key;

use rand::Rng;
use rand::thread_rng;

use serenity::Result;
use serenity::client::{Client, Context};
use serenity::model;
use serenity::utils;

const PREFIX: &str = "f>";

command!(info(_ctx, msg, _args) {
    msg.channel_id.send_message(|cm| {
        cm.embed(|e| {
            e.title("lfalchbot")
             .colour(utils::Colour::blue())
             .description("(c) LFalch.com 2017")
             .footer(|f| f.text(serenity::constants::USER_AGENT))
        })
    }).unwrap();
});

command!(setgame(ctx, _msg, args) {
    ctx.set_game(model::Game::playing(&args.join(" ")));
});

command!(rpn(_ctx, msg, args) {
    match calculate(&args.join(" ")) {
        Ok(r) => msg.reply(&format!("Result: {}", r)),
        Err(e) => msg.reply(&format!("Error: {:?}", e))
    }.unwrap();
});

const CSGO_MSGS: [&str; 5] = [
    "Vi varmer op med en comp!",
    "Jeg er på!",
    "Jeg er mere til Call of Duty ..",
    "Сука блядь!",
    "Mongoskrald!"
];

const BEARTOOTH: [&str; 3] = [
    "I’m not useless! I’m just the king of excuses!",
    "One life and one decision! Make sure it ends with you still living!",
    "Lorteskat på T-shirts."
];

const RESPONSES: [&str; 4] = [
    "Undskyld, kan ikke snakke lige nu :(",
    "Hey, kan jeg ringe igen senere?",
    "Hva' så, din noob!? :P",
    "Ad, hvem er du?"
];

const REDALERT: [&str; 4] = [
    "Your base is under a salt!",
    "Det gamle lortespil?",
    "Jeg er mere til CSGO.",
    "\"Mine depleted\" ..."
];

struct BotUser;

impl Key for BotUser {
    type Value = String;
}

fn main() {
    let token = env::var("DISCORD_TOKEN")
        .expect("Expected a token in the environment");
    let mut client = Client::new(&token);

    client.with_framework(|f| f
        .configure(|c| c.prefix(PREFIX))
        .command("ping", |c| c.exec_str("Pong!"))
        .on("info", info)
        .on("setgame", setgame)
        .on("rpn", rpn)
    );

    {
        let mut data = client.data.lock().unwrap();
        data.insert::<BotUser>(String::default());
    }

    client.on_ready(|ctx, ready| {
        println!("{} is connected!", ready.user.name);
        println!("Guilds:");
        for name in ready.guilds.iter().map(|g| g.id().get().unwrap().name) {
            println!("    {}", name);
        }
        {
            let mut data = ctx.data.lock().unwrap();
            *data.get_mut::<BotUser>().unwrap() = ready.user.tag();
        }
    });

    client.on_message(on_message);

    if let Err(why) = client.start() {
        println!("Client error: {:?}", why);
    }
}

fn send_random(chl: model::ChannelId, list: &[&str]) -> Result<model::Message> {
    let i = thread_rng().gen_range(0, list.len());
    chl.say(list[i])
}

macro_rules! joke {
    ($s:expr; $($trigger:expr),+; $bl:block) => (
        if $($s.contains($trigger))||* $bl
    );
    ($s:expr, $channel_id:expr; $($trigger:expr),+;; $joke:expr) => (
        joke!($s; $($trigger),*; {
            $channel_id.say($joke).unwrap();
        })
    );
    ($s:expr, $channel_id:expr; $($trigger:expr),+; $jokes:expr) => (
        joke!($s; $($trigger),*; {
            send_random($channel_id, &$jokes).unwrap();
        })
    );
}

fn on_message(ctx: Context, msg: model::Message) {
    if msg.author.bot {
        return
    }
    let s: String = msg.content.chars()
        .filter(|c| c.is_alphanumeric())
        .flat_map(|c|c.to_lowercase())
        .collect();

    joke!(s, msg.channel_id; "css", "source";; "Hvor er mine skins!?");
    joke!(s, msg.channel_id; "csgo", "counterstrike", "globaloffensive"; CSGO_MSGS);
    joke!(s, msg.channel_id; "mc", "minecraft";; "MINECRAFT!");
    joke!(s, msg.channel_id; "beartooth"; BEARTOOTH);
    joke!(s, msg.channel_id; "rep";; "Rep mig!");
    joke!(s, msg.channel_id; "ftl";; "Zoltan shield OP");
    joke!(s, msg.channel_id; "bindingofisaac";; "Mom OP");
    joke!(s, msg.channel_id; "meme";; "krydrede migmig'er");
    joke!(s, msg.channel_id; "gunsoficarus";; "Spillere online: 85");
    joke!(s, msg.channel_id; "doom";; "Rip and tear!");
    joke!(s, msg.channel_id; "dyinglight";; "Det dér Left 4 Dead-spil?");
    joke!(s; "english"; {
        msg.channel_id.send_message(|cm| {
            cm.embed(|e| {
                e.image("http://dev.lfalch.com/english.jpg")
            })
        }).unwrap();
    });
    joke!(s, msg.channel_id; "ra3", "redalert"; REDALERT);
    joke!(s, msg.channel_id; "rusland", "russia", "росси", "russisk",
        "russian", "русск", "russer";; "Communism is the ultimate goal of socialism.");

    let user = {
        ctx.data.lock().unwrap().get::<BotUser>().unwrap().clone()
    };
    if msg.mentions.iter().map(|u| u.tag()).any(|u| u == user) {
        send_random(msg.channel_id, &RESPONSES).unwrap();
    }
}

#[derive(Debug, Copy, Clone)]
pub enum RpnError {
    StackTooSmall,
    UnknownOperator(char),
    NoOperands
}

pub fn calculate(calculation: &str) -> StdResult<f64, RpnError>{
    let mut stack = Vec::new();

    let operations = calculation.trim().split(' ');

    for operation in operations {
        let d = operation.parse::<f64>().ok();
        if let Some(d) = d {
            stack.push(d);
        }else{
            calc(operation.chars().next().unwrap(), &mut stack)?
        }
    }

    stack.pop().ok_or(RpnError::NoOperands)
}

fn calc(op: char, stack: &mut Vec<f64>) -> StdResult<(), RpnError>{
    match (stack.pop(), stack.pop()){
        (Some(op1), Some(op2)) => match op{
            '+' => stack.push(op2 + op1),
            '-' => stack.push(op2 - op1),
            '/' => stack.push(op2 / op1),
            '*' => stack.push(op2 * op1),
            '^' => stack.push(op2.powf(op1)),
            _ => return Err(RpnError::UnknownOperator(op))
        },
        _ => return Err(RpnError::StackTooSmall)
    }
    Ok(())
}
<|endoftext|>