<commit_before>//! Loads the configuration and runs the game workflow.
extern crate crossterm;
extern crate rand;
extern crate thiserror;
mod game;
use game::{Game, State};
mod user_interface;
use user_interface::UserInterface;
mod dictionary;
use dictionary::Dict;
mod image;
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Write;
use std::path::PathBuf;
use std::process;

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
const AUTHOR: &str = "(c) Jens Getreu, 2016-2020.";

/// Text to show as command-line help --help
const COMMANDLINE_HELP: &str = r#"
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
ASCII-ART HANGMAN FOR KIDS

         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (")
    `-j                |
      `-._(           /
         |_\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lives:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: ascii-hangman
        ascii-hangman [FILE]...
        ascii-hangman -h|--help
        asciiart-ascii-hangman-for-kids -V|--version

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to `ascii-hangman-words.txt`. In
case no `[FILE]` is found, a template configuration file `ascii-hangman-words.txt` is
written into the current working directory. Multiple `[FILE]`s are concatenated.

`[FILE]` is a UTF-8 file containing 4 different line-types:

- lines starting with a letter, a digit or '-' are secret strings. At the
  beginning of the game one line is randomly chosen and all characters are
  hidden. If you want to give an additional hint, enclose some characters
  with `_`.  The enclosed is then displayed in clear when the game starts.
  For example the configuration line:

      Guess _me_

  is shown in the game as:

      _ _ _ _ _ _ m e

- lines starting with `#` are ignored. This can be used for comments.

- lines starting with `|` are part of an optional custom ASCII-Art image shown
  progressively in the course of the game. If not defined here, built in
  ASCII-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:

   `:success-rewarding`       Every guessed character shows a bit more of
                              the image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                              image. Choose this mode together with a
                              traditional gallows image (not built in).

The following shows an example of a custom image (copy it left-aligned
into the config-file):

        |  ,~~--~~-.
        | +      | |\
        | || |~ |`,/-\
        | *\_) \_) `-'

If you prefer a traditional gallows image, add the following:

        :traditional-rewarding
        |  ______
        |  |    |
        |  |    O
        |  |   /|\
        |  |    |
        |  |   / \
        |__|_____
        ||      |___
        ||_________|

"#;

/// Number of wrong guess allowed.
const LIVES: u8 = 7;
/// Default configuration filename when no filename is given at the command-line.
const PATHSTR: &str = "ascii-hangman-words.txt";
/// Fallback sample configuration when no configuration file can be found.
const CONF_TEMPLATE: &str =
    "# Type `ascii-hangman -h` to learn how to insert custom ASCII-art images here. \r
\r
guess me\r
hang_man_\r
_good l_uck\r
_3*_7_=21_\r
";

/// Fallback secret when no configuration file can be found.
const CONF_DEMO: &str = "- _Demo: add own words to config file and start a_gain_!";

// ------------------ MAIN ---------------------------------------------

/// Reads the configuration file.
pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
    let mut f = File::open(pathstr)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

/// Writes a sample configuration file on disk. Called when no configuration file can be found.
pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

/// Starts the game.
#[allow(unused_labels)]
fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            eprintln!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(ref a) if a == "-V" || a == "--version" => {
            eprintln!("{}", VERSION.unwrap());
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths = env::args()
        .skip(1)
        .map(|s| PathBuf::from(s))
        .collect::<Vec<PathBuf>>();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.is_empty() {
        conf_file_paths.push(PathBuf::from(PATHSTR))
    };

    // read and concatenate all config files given on command line
    let cwd = env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {
        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s) => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        eprintln!(
                            "As no config-file :\n\
                             \t{:?}\n\
                             was found a template file is written in the \
                             current working directory.\n\
                             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                            path, cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        eprintln!(
                            "Couldn't write ascii-hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            why.to_string(),
                            cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE GAME

    let mut ui = match UserInterface::new(&config) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("ERROR IN CONFIGURATION FILE\n{}", e);

            // wait for [Enter] key
            let s = &mut String::new();
            io::stdin().read_line(s).unwrap();
            process::exit(1);
        }
    };

    let mut dict = match Dict::new(&config) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("ERROR IN CONFIGURATION FILE\n{}", e);

            // wait for [Enter] key
            let s = &mut String::new();
            io::stdin().read_line(s).unwrap();
            process::exit(1);
        }
    };

    // PLAY

    'playing: loop {
        let secret = match &mut dict.get_random_secret() {
            None => break 'playing,
            Some(w) => w.to_owned(),
        };
        let mut game = Game::new(&secret, LIVES, dict.is_empty());

        // The game loop
        'running_game: loop {
            let answer = ui.render(&game);

            match game.state {
                State::Victory => {
                    let a = answer.chars().next().unwrap_or('Y');
                    if a == 'N' || a == 'n' {
                        break 'playing;
                    } else {
                        continue 'playing;
                    }
                }
                State::VictoryGameOver => {
                    break 'playing;
                }
                State::Defeat | State::DefeatGameOver => {
                    // We will ask this again, so we never end the round with a defeat.
                    dict.add(secret.clone());

                    let a = answer.chars().next().unwrap_or('Y');
                    if a == 'N' || a == 'n' {
                        break 'playing;
                    } else {
                        continue 'playing;
                    }
                }
                State::Ongoing => {
                    game.guess(answer.chars().next().unwrap_or(' '));
                }
            }
        }
    }

    println!("\n{}", AUTHOR);
}
<commit_msg>more idiomatic loop<commit_after>//! Loads the configuration and runs the game workflow.
extern crate crossterm;
extern crate rand;
extern crate thiserror;
mod game;
use game::{Game, State};
mod user_interface;
use user_interface::UserInterface;
mod dictionary;
use dictionary::Dict;
mod image;
use std::env;
use std::fs::File;
use std::io;
use std::io::prelude::*;
use std::io::Write;
use std::path::PathBuf;
use std::process;

const VERSION: Option<&'static str> = option_env!("CARGO_PKG_VERSION");
const AUTHOR: &str = "(c) Jens Getreu, 2016-2020.";

/// Text to show as command-line help --help
const COMMANDLINE_HELP: &str = r#"
Hangman is a paper and pencil guessing game for two or more players.  One player
thinks of a word, phrase or sentence and the other tries to guess it by
suggesting letters or numbers, within a certain number of guesses. In this
version for children the computer selects a word, phrase or sentence randomly
out of a word-list defined in a configuration file. In the course of the game
Ascii-Art images - designed for children - are progressively disclosed.

===================================
-----------------------------------
ASCII-ART HANGMAN FOR KIDS

         ,.
        (_|,.
       ,' /, )_______
    __j o``-'        `
   (")
    `-j                |
      `-._(           /
         |_\  |--^.  /
        /_]'|_| /_)_/
           /_]'  /_]'

Lives:  1       Last guess: 3

 g o o _ _ l _ _ k

Type a letter then type [Enter]:
-----------------------------------
===================================

 Usage: ascii-hangman
        ascii-hangman [FILE]...
        ascii-hangman -h|--help
        asciiart-ascii-hangman-for-kids -V|--version

`[FILE]` are configuration files containing word-lists and optionally Ascii-Art
images.

When no `[FILE]` argument is given, `[FILE]` defaults to `ascii-hangman-words.txt`. In
case no `[FILE]` is found, a template configuration file `ascii-hangman-words.txt` is
written into the current working directory. Multiple `[FILE]`s are concatenated.

`[FILE]` is a UTF-8 file containing 4 different line-types:

- lines starting with a letter, a digit or '-' are secret strings. At the
  beginning of the game one line is randomly chosen and all characters are
  hidden. If you want to give an additional hint, enclose some characters
  with `_`.  The enclosed is then displayed in clear when the game starts.
  For example the configuration line:

      Guess _me_

  is shown in the game as:

      _ _ _ _ _ _ m e

- lines starting with `#` are ignored. This can be used for comments.

- lines starting with `|` are part of an optional custom ASCII-Art image shown
  progressively in the course of the game. If not defined here, built in
  ASCII-Art images are used instead.

- lines starting with `:` are game modifier. They change the logic how the image
  is progressively disclosed:

   `:success-rewarding`       Every guessed character shows a bit more of
                              the image. This mode is default.
   `:traditional-rewarding`   Every lost live discloses a bit more of the
                              image. Choose this mode together with a
                              traditional gallows image (not built in).

The following shows an example of a custom image (copy it left-aligned
into the config-file):

        |  ,~~--~~-.
        | +      | |\
        | || |~ |`,/-\
        | *\_) \_) `-'

If you prefer a traditional gallows image, add the following:

        :traditional-rewarding
        |  ______
        |  |    |
        |  |    O
        |  |   /|\
        |  |    |
        |  |   / \
        |__|_____
        ||      |___
        ||_________|

"#;

/// Number of wrong guess allowed.
const LIVES: u8 = 7;
/// Default configuration filename when no filename is given at the command-line.
const PATHSTR: &str = "ascii-hangman-words.txt";
/// Fallback sample configuration when no configuration file can be found.
const CONF_TEMPLATE: &str =
    "# Type `ascii-hangman -h` to learn how to insert custom ASCII-art images here. \r
\r
guess me\r
hang_man_\r
_good l_uck\r
_3*_7_=21_\r
";

/// Fallback secret when no configuration file can be found.
const CONF_DEMO: &str = "- _Demo: add own words to config file and start a_gain_!";

// ------------------ MAIN ---------------------------------------------

/// Reads the configuration file.
pub fn read_config(pathstr: &PathBuf) -> Result<String, io::Error> {
    let mut f = File::open(pathstr)?;
    let mut s = String::new();
    f.read_to_string(&mut s)?;
    Ok(s)
}

/// Writes a sample configuration file on disk. Called when no configuration file can be found.
pub fn write_config_template(pathstr: &PathBuf) -> Result<(), io::Error> {
    let mut file = File::create(&pathstr)?;
    file.write_all(CONF_TEMPLATE.as_bytes())?;
    Ok(())
}

/// Starts the game.
#[allow(unused_labels)]
fn main() {
    // SHOW HELP TEXT
    match env::args().nth(1) {
        Some(ref a) if a == "-h" || a == "--help" => {
            eprintln!("{}", COMMANDLINE_HELP);
            return;
        }
        Some(ref a) if a == "-V" || a == "--version" => {
            eprintln!("{}", VERSION.unwrap());
            return;
        }
        Some(_) | None => {}
    };

    // READ CONFIG

    // Read all config files given on command line
    let mut conf_file_paths = env::args()
        .skip(1)
        .map(|s| PathBuf::from(s))
        .collect::<Vec<PathBuf>>();

    // if no conf_file_paths are given then use default config path
    if conf_file_paths.is_empty() {
        conf_file_paths.push(PathBuf::from(PATHSTR))
    };

    // read and concatenate all config files given on command line
    let cwd = env::current_dir().unwrap();

    let mut config: String = String::new();
    for conf_file_path in &conf_file_paths {
        let path = conf_file_path;
        let c = match read_config(&path) {
            Ok(s) => s,
            Err(_) => {
                match write_config_template(&path) {
                    Ok(_) => {
                        eprintln!(
                            "As no config-file :\n\
                             \t{:?}\n\
                             was found a template file is written in the \
                             current working directory.\n\
                             \t{:?}\n\n\nPress [Enter] to enter demo mode.",
                            path, cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                    Err(why) => {
                        eprintln!(
                            "Couldn't write ascii-hangman template \
                             config-file:\n\t{:?}\n({})\n\n\
                             Current working directory is:\n\t{:?}\n\n\
                             Press [Enter] to enter demo mode.",
                            path,
                            why.to_string(),
                            cwd
                        );
                        // wait for [Enter] key
                        let s = &mut String::new();
                        io::stdin().read_line(s).unwrap();
                        CONF_DEMO.to_string()
                    }
                }
            }
        };
        config.push_str(&c);
    }

    // INITIALISE GAME

    let mut ui = match UserInterface::new(&config) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("ERROR IN CONFIGURATION FILE\n{}", e);

            // wait for [Enter] key
            let s = &mut String::new();
            io::stdin().read_line(s).unwrap();
            process::exit(1);
        }
    };

    let mut dict = match Dict::new(&config) {
        Ok(d) => d,
        Err(e) => {
            eprintln!("ERROR IN CONFIGURATION FILE\n{}", e);

            // wait for [Enter] key
            let s = &mut String::new();
            io::stdin().read_line(s).unwrap();
            process::exit(1);
        }
    };

    // PLAY

    'playing: while let Some(secret) = dict.get_random_secret() {
        let mut game = Game::new(&secret, LIVES, dict.is_empty());

        // The game loop
        'running_game: loop {
            let answer = ui.render(&game);

            match game.state {
                State::Victory => {
                    let a = answer.chars().next().unwrap_or('Y');
                    if a == 'N' || a == 'n' {
                        break 'playing;
                    } else {
                        continue 'playing;
                    }
                }
                State::VictoryGameOver => {
                    break 'playing;
                }
                State::Defeat | State::DefeatGameOver => {
                    // We will ask this again, so we never end the round with a defeat.
                    dict.add(secret.clone());

                    let a = answer.chars().next().unwrap_or('Y');
                    if a == 'N' || a == 'n' {
                        break 'playing;
                    } else {
                        continue 'playing;
                    }
                }
                State::Ongoing => {
                    game.guess(answer.chars().next().unwrap_or(' '));
                }
            }
        }
    }

    println!("\n{}", AUTHOR);
}
<|endoftext|>


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

extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate serialize;

use neovim::*;
use rgtk::*;
use std::collections::HashSet;

mod projects;
mod ui;
mod utils;

mod ffi {
    use libc::c_int;

    extern "C" {
        pub fn fork () -> c_int;
        pub fn kill (pid: c_int, sig: c_int);
    }
}

fn start_gui(pty: &mut gtk::VtePty, pid: i32) {
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;

    // create the window

    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(|_| {
        unsafe { ffi::kill(pid, 15); }
        gtk::main_quit();
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut proj_btns = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    proj_btns.set_size_request(-1, -1);
    proj_btns.add(&new_button);
    proj_btns.add(&import_button);
    proj_btns.add(&rename_button);
    proj_btns.add(&remove_button);

    let mut proj_tree = gtk::TreeView::new().unwrap();
    let selection = proj_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    proj_tree.set_model(&model);
    proj_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&proj_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    proj_tree.append_column(&column);

    let mut proj_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    proj_pane.set_size_request(-1, -1);
    proj_pane.pack_start(&proj_btns, false, true, 0);
    proj_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_btns = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_btns.set_size_request(-1, -1);
    build_btns.add(&run_button);
    build_btns.add(&build_button);
    build_btns.add(&test_button);
    build_btns.add(&clean_button);
    build_btns.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_btns, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&proj_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
    };

    ::utils::create_data_dir();
    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut proj_tree);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::new_project(&mut state, &mut proj_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::import_project(&mut state, &mut proj_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(|| {
        ::projects::update_selection(&mut state);
    }));
    proj_tree.connect(gtk::signals::RowCollapsed::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    proj_tree.connect(gtk::signals::RowExpanded::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();
    gtk::main();
}

fn main() {
    let mut pty = gtk::VtePty::new().unwrap();
    let pid = unsafe { ffi::fork() };

    if pid > 0 {
        start_gui(&mut pty, pid);
    } else {
        pty.child_setup();
        nvim_main();
    }
}
<commit_msg>Remove  feature flag<commit_after>#![feature(globs)]

extern crate libc;
extern crate neovim;
extern crate rgtk;
extern crate serialize;

use neovim::*;
use rgtk::*;
use std::collections::HashSet;

mod projects;
mod ui;
mod utils;

mod ffi {
    use libc::c_int;

    extern "C" {
        pub fn fork () -> c_int;
        pub fn kill (pid: c_int, sig: c_int);
    }
}

fn start_gui(pty: &mut gtk::VtePty, pid: i32) {
    gtk::init();

    // constants

    let width = 1242;
    let height = 768;
    let editor_height = ((height as f32) * 0.75) as i32;

    // create the window

    let mut window = gtk::Window::new(gtk::WindowType::TopLevel).unwrap();
    window.set_title("SolidOak");
    window.set_window_position(gtk::WindowPosition::Center);
    window.set_default_size(width, height);

    window.connect(gtk::signals::DeleteEvent::new(|_| {
        unsafe { ffi::kill(pid, 15); }
        gtk::main_quit();
        true
    }));

    // create the panes

    let new_button = gtk::Button::new_with_label("New Project").unwrap();
    let import_button = gtk::Button::new_with_label("Import").unwrap();
    let rename_button = gtk::Button::new_with_label("Rename").unwrap();
    let remove_button = gtk::Button::new_with_label("Remove").unwrap();

    let mut proj_btns = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    proj_btns.set_size_request(-1, -1);
    proj_btns.add(&new_button);
    proj_btns.add(&import_button);
    proj_btns.add(&rename_button);
    proj_btns.add(&remove_button);

    let mut proj_tree = gtk::TreeView::new().unwrap();
    let selection = proj_tree.get_selection().unwrap();
    let column_types = [glib::ffi::g_type_string, glib::ffi::g_type_string];
    let store = gtk::TreeStore::new(&column_types).unwrap();
    let model = store.get_model().unwrap();
    proj_tree.set_model(&model);
    proj_tree.set_headers_visible(false);

    let mut scroll_pane = gtk::ScrolledWindow::new(None, None).unwrap();
    scroll_pane.add(&proj_tree);

    let column = gtk::TreeViewColumn::new().unwrap();
    let cell = gtk::CellRendererText::new().unwrap();
    column.pack_start(&cell, true);
    column.add_attribute(&cell, "text", 0);
    proj_tree.append_column(&column);

    let mut proj_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    proj_pane.set_size_request(-1, -1);
    proj_pane.pack_start(&proj_btns, false, true, 0);
    proj_pane.pack_start(&scroll_pane, true, true, 0);

    let mut editor_pane = gtk::VteTerminal::new().unwrap();
    editor_pane.set_size_request(-1, editor_height);
    editor_pane.set_pty(pty);
    editor_pane.watch_child(pid);

    let run_button = gtk::Button::new_with_label("Run").unwrap();
    let build_button = gtk::Button::new_with_label("Build").unwrap();
    let test_button = gtk::Button::new_with_label("Test").unwrap();
    let clean_button = gtk::Button::new_with_label("Clean").unwrap();
    let stop_button = gtk::Button::new_with_label("Stop").unwrap();

    let mut build_btns = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    build_btns.set_size_request(-1, -1);
    build_btns.add(&run_button);
    build_btns.add(&build_button);
    build_btns.add(&test_button);
    build_btns.add(&clean_button);
    build_btns.add(&stop_button);

    let build_term = gtk::VteTerminal::new().unwrap();

    let mut build_pane = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    build_pane.pack_start(&build_btns, false, true, 0);
    build_pane.pack_start(&build_term, true, true, 0);

    let mut content = gtk::Box::new(gtk::Orientation::Vertical, 0).unwrap();
    content.pack_start(&editor_pane, false, true, 0);
    content.pack_start(&build_pane, true, true, 0);

    let mut hbox = gtk::Box::new(gtk::Orientation::Horizontal, 0).unwrap();
    hbox.pack_start(&proj_pane, false, true, 0);
    hbox.pack_start(&content, true, true, 0);
    window.add(&hbox);

    // populate the project tree

    let mut state = ::utils::State{
        projects: HashSet::new(),
        expansions: HashSet::new(),
        selection: None,
        tree_model: &model,
        tree_store: &store,
        tree_selection: &selection,
        rename_button: &rename_button,
        remove_button: &remove_button,
    };

    ::utils::create_data_dir();
    ::utils::read_prefs(&mut state);
    ::ui::update_project_tree(&mut state, &mut proj_tree);

    // connect to the signals

    new_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::new_project(&mut state, &mut proj_tree);
    }));
    import_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::import_project(&mut state, &mut proj_tree);
    }));
    rename_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::rename_file(&mut state);
    }));
    remove_button.connect(gtk::signals::Clicked::new(|| {
        ::projects::remove_item(&mut state);
    }));
    selection.connect(gtk::signals::Changed::new(|| {
        ::projects::update_selection(&mut state);
    }));
    proj_tree.connect(gtk::signals::RowCollapsed::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::remove_expansion(&mut state, &iter);
    }));
    proj_tree.connect(gtk::signals::RowExpanded::new(|iter_raw, _| {
        let iter = gtk::TreeIter::wrap_pointer(iter_raw);
        ::projects::add_expansion(&mut state, &iter);
    }));

    // show the window

    window.show_all();
    gtk::main();
}

fn main() {
    let mut pty = gtk::VtePty::new().unwrap();
    let pid = unsafe { ffi::fork() };

    if pid > 0 {
        start_gui(&mut pty, pid);
    } else {
        pty.child_setup();
        nvim_main();
    }
}
<|endoftext|>


<commit_before>extern crate futures;
extern crate tokio_core;
extern crate tokio_io;

extern crate tokio_process;

extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate toml;

extern crate regex;

#[macro_use]
extern crate error_chain;

extern crate telegram_bot_client;
use telegram_bot_client::{BotFactory, Update, Bot};

extern crate telegram_bot_types;
use telegram_bot_types as types;

use tokio_core::reactor;
use tokio_core::reactor::Handle;
use tokio_io::io;
use futures::{Future, Stream};
use futures::future;

use std::process::{Command, Stdio};
use tokio_process::CommandExt;

extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "overbot", about = "A telegram bot manager")]
struct Opt {
    #[structopt(help = "Config files directory")]
    config_dir: String,
}

mod config;
mod errors;

fn handle_message(
    handle: Handle,
    bot: Bot,
    config: &config::Bot,
    msg: serde_json::Value,
) -> Box<Future<Item = (), Error = errors::Error>> {
    let original = msg.clone();
    let msg: types::response::Message =
        serde_json::from_value(msg).expect("Unexpected message format");
    let text = msg.text.clone().unwrap_or(String::new());
    let cmd = config
        .commands
        .iter()
        .filter(|c| {
            c.allowed(msg.from.id) && c.allowed(msg.chat.id) && c.regex.is_match(&text) &&
                c.mode == config::Mode::Message
        })
        .nth(0);
    let cmd = match cmd {
        Some(c) => c.clone(),
        None => return Box::new(future::ok(())),
    };
    let args: Vec<String> = {
        let captures = cmd.regex.captures(&text).expect(
            "we already checked for match",
        );
        cmd.args
            .iter()
            .map(|a| {
                let mut buf = String::new();
                captures.expand(a, &mut buf);
                buf
            })
            .collect()
    };
    let child = Command::new(&cmd.executable)
        .args(&args)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn_async(&handle);
    let mut child = match child {
        Ok(c) => c,
        Err(e) => return Box::new(future::err(e.into())),
    };
    let stdin = child.stdin().take().unwrap();
    let res = match cmd.input {
        config::InputType::Text => io::write_all(stdin, text.into_bytes()),
        config::InputType::Json => io::write_all(stdin, serde_json::to_vec(&original).unwrap()),
    };
    let res = res.map_err(|e| e.into()).and_then(|_| {
        child.wait_with_output().map_err(|e| e.into()).and_then(
            move |out| {
                let mut out = String::from_utf8(out.stdout).unwrap();
                println!("out: {:?}", out);
                if cmd.output == config::OutputType::TextMono {
                    out = format!("```{}```", out);
                }
                let work;
                if cmd.output == config::OutputType::Json {
                    let json: Result<serde_json::Value, _> = serde_json::from_str(&out);
                    let json = match json {
                        Ok(j) => j,
                        Err(e) => return future::err(e.into()),
                    };
                    work = bot.request::<_, serde_json::Value>("sendMessage", &json);
                } else {
                    let parse_mode = match cmd.output {
                        config::OutputType::Text => types::request::ParseMode::Text,
                        config::OutputType::TextMono => types::request::ParseMode::Markdown,
                        config::OutputType::Markdown => types::request::ParseMode::Markdown,
                        config::OutputType::Html => types::request::ParseMode::Html,
                        config::OutputType::Json => unreachable!(),
                    };
                    work = bot.request::<_, serde_json::Value>(
                        "sendMessage",
                        &serde_json::to_value(
                            types::request::Message::new(msg.chat.id, out).parse_mode(
                                parse_mode,
                            ),
                        ).unwrap(),
                    );
                }
                let work = work.and_then(|r| {
                    println!("{:?}", r);
                    future::ok(())
                }).map_err(|e| println!("error: {:?}", e));
                handle.spawn(work);
                future::ok(())
            },
        )
    });
    Box::new(res)
}

fn handle_inline_query(
    handle: Handle,
    bot: Bot,
    config: &config::Bot,
    query: serde_json::Value,
) -> Box<Future<Item = (), Error = errors::Error>> {
    let original = query.clone();
    let query: types::response::InlineQuery =
        serde_json::from_value(query).expect("malformed message");
    let offset = query.offset.parse::<i32>().unwrap_or(0);
    let cmd = config
        .commands
        .iter()
        .filter(|c| {
            c.allowed(query.from.id) && c.regex.is_match(&query.query) &&
                c.mode == config::Mode::Inline
        })
        .nth(0);
    let cmd = match cmd {
        Some(c) => c.clone(),
        None => return Box::new(future::ok(())),
    };
    let args: Vec<String> = {
        let captures = cmd.regex.captures(&query.query).expect(
            "we already checked for match",
        );
        cmd.args
            .iter()
            .map(|a| {
                //Ugly hack part 1
                let a = a.replace("${offset}", "$${offset}");
                let mut buf = String::new();
                captures.expand(&a, &mut buf);
                //Ugly hack part 2
                buf.replace("$${offset}", "${offset}")
            })
            .collect()
    };
    let query_text = query.query.clone();
    let h = handle.clone();
    let batch_size = 10;
    let cmd_clone = cmd.clone();
    let res = future::loop_fn(
        (0, Vec::new()),
        move |(iter, mut answers)| -> Box<Future<Item = future::Loop<_, _>, Error = errors::Error>> {
            if iter == batch_size - 1 {
                return Box::new(future::ok(future::Loop::Break((iter, answers))));
            }
            let args: Vec<_> = args.iter()
                .map(|a| a.replace("${offset}", &(offset + iter).to_string()))
                .collect();
            let child = Command::new(&cmd.executable)
                .args(&args)
                .stdin(Stdio::piped())
                .stdout(Stdio::piped())
                .spawn_async(&h);
            let mut child = match child {
                Ok(c) => c,
                Err(e) => return Box::new(future::err(e.into())),
            };
            let stdin = child.stdin().take().unwrap();
            let res = match cmd.input {
                config::InputType::Text => io::write_all(stdin, query_text.clone().into_bytes()),
                config::InputType::Json => {
                    io::write_all(stdin, serde_json::to_vec(&original).unwrap())
                }
            };
            Box::new(res.map_err(|e| e.into()).and_then(move |_| {
                child.wait_with_output().map_err(|e| e.into()).and_then(
                    move |out| {
                        let out = String::from_utf8(out.stdout).unwrap();
                        answers.push(out);
                        Ok(future::Loop::Continue((iter + 1, answers)))
                    },
                )
            }))
        },
    );
    let res = res.and_then(move |(_, mut results)| {
        results.retain(|a| !a.is_empty());
        let new_offset = offset + results.len() as i32;
        let results: Vec<serde_json::Value> = {
            if cmd_clone.output == config::OutputType::Json {
                let mut new_results = Vec::new();
                let mut i = 0;
                for r in results {
                    let json: Result<serde_json::Value, _> = serde_json::from_str(&r);
                    let r = match json {
                        Ok(mut j) => {
                            match j.as_object_mut() {
                                Some(mut o) => {
                                    o.insert("id".to_owned(), json!((offset + i).to_string()))
                                }
                                None => {
                                    return future::err(
                                        errors::ErrorKind::Output(
                                            cmd_clone.executable,
                                            "Expected a json object",
                                        ).into(),
                                    )
                                }
                            };
                            j
                        }
                        Err(e) => return future::err(e.into()),
                    };
                    new_results.push(r);
                    i += 1;
                }
                new_results
            } else {
                results
                    .into_iter()
                    .enumerate()
                    .map(|(i, a)| {
                        let i = i as i32;
                        serde_json::to_value(types::request::InlineQueryResult::article(
                            (offset + i).to_string(),
                            a.clone(),
                            a.clone(),
                        )).expect("cannot convert to Value")
                    })
                    .collect()
            }
        };
        let answer =
            types::request::AnswerInlineQuery::new(query.id, results, new_offset.to_string());
        let work = bot.request::<_, serde_json::Value>(
            "answerInlineQuery",
            &serde_json::to_value(answer).unwrap(),
        );
        let work = work.and_then(|r| {
            println!("{:?}", r);
            future::ok(())
        }).map_err(|e| println!("error: {:?}", e));
        handle.spawn(work);
        future::ok(())
    });
    Box::new(res)
}
fn main() {
    let opt = Opt::from_args();
    let config = config::get_all(&opt.config_dir).expect("Wrong path for config dir");

    let mut event_loop = reactor::Core::new().unwrap();
    let handle = event_loop.handle();

    let factory = BotFactory::new(handle.clone());
    let work = future::join_all(config.into_iter().map(|config_bot| {
        let handle = handle.clone();
        let (bot, updates) = factory.new_bot(&config_bot.token);
        updates
            .from_err()
            .and_then(move |update| {
                println!("{:?}", update);
                match update {
                    Update::Message(msg) => {
                        handle_message(handle.clone(), bot.clone(), &config_bot, msg)
                    }
                    Update::InlineQuery(query) => {
                        handle_inline_query(handle.clone(), bot.clone(), &config_bot, query)
                    }
                    _ => Box::new(future::ok(())),

                }
            })
            .or_else(|e| {
                println!("Error: {:?}", e);
                future::ok::<_, ()>(())
            })
            .for_each(|_| future::ok(()))
    }));
    event_loop.run(work).expect("Unexpected failure");
}
<commit_msg>remove all unwrap() in favor of expect()<commit_after>extern crate futures;
extern crate tokio_core;
extern crate tokio_io;

extern crate tokio_process;

extern crate serde;
#[macro_use]
extern crate serde_derive;
#[macro_use]
extern crate serde_json;
extern crate toml;

extern crate regex;

#[macro_use]
extern crate error_chain;

extern crate telegram_bot_client;
use telegram_bot_client::{BotFactory, Update, Bot};

extern crate telegram_bot_types;
use telegram_bot_types as types;

use tokio_core::reactor;
use tokio_core::reactor::Handle;
use tokio_io::io;
use futures::{Future, Stream};
use futures::future;

use std::process::{Command, Stdio};
use tokio_process::CommandExt;

extern crate structopt;
#[macro_use]
extern crate structopt_derive;

use structopt::StructOpt;

#[derive(StructOpt, Debug)]
#[structopt(name = "overbot", about = "A telegram bot manager")]
struct Opt {
    #[structopt(help = "Config files directory")]
    config_dir: String,
}

mod config;
mod errors;

fn handle_message(
    handle: Handle,
    bot: Bot,
    config: &config::Bot,
    msg: serde_json::Value,
) -> Box<Future<Item = (), Error = errors::Error>> {
    let original = msg.clone();
    let msg: types::response::Message =
        serde_json::from_value(msg).expect("Unexpected message format");
    let text = msg.text.clone().unwrap_or(String::new());
    let cmd = config
        .commands
        .iter()
        .filter(|c| {
            c.allowed(msg.from.id) && c.allowed(msg.chat.id) && c.regex.is_match(&text) &&
                c.mode == config::Mode::Message
        })
        .nth(0);
    let cmd = match cmd {
        Some(c) => c.clone(),
        None => return Box::new(future::ok(())),
    };
    let args: Vec<String> = {
        let captures = cmd.regex.captures(&text).expect(
            "we already checked for match",
        );
        cmd.args
            .iter()
            .map(|a| {
                let mut buf = String::new();
                captures.expand(a, &mut buf);
                buf
            })
            .collect()
    };
    let child = Command::new(&cmd.executable)
        .args(&args)
        .stdin(Stdio::piped())
        .stdout(Stdio::piped())
        .spawn_async(&handle);
    let mut child = match child {
        Ok(c) => c,
        Err(e) => return Box::new(future::err(e.into())),
    };
    let stdin = child.stdin().take().expect("no stdin for the process");
    let res = match cmd.input {
        config::InputType::Text => io::write_all(stdin, text.into_bytes()),
        config::InputType::Json => io::write_all(stdin, serde_json::to_vec(&original).expect("cannot serialize")),
    };
    let res = res.map_err(|e| e.into()).and_then(|_| {
        child.wait_with_output().map_err(|e| e.into()).and_then(
            move |out| {
                let mut out = String::from_utf8(out.stdout).expect("encoding is not utf8");
                println!("out: {:?}", out);
                if cmd.output == config::OutputType::TextMono {
                    out = format!("```{}```", out);
                }
                let work;
                if cmd.output == config::OutputType::Json {
                    let json: Result<serde_json::Value, _> = serde_json::from_str(&out);
                    let json = match json {
                        Ok(j) => j,
                        Err(e) => return future::err(e.into()),
                    };
                    work = bot.request::<_, serde_json::Value>("sendMessage", &json);
                } else {
                    let parse_mode = match cmd.output {
                        config::OutputType::Text => types::request::ParseMode::Text,
                        config::OutputType::TextMono => types::request::ParseMode::Markdown,
                        config::OutputType::Markdown => types::request::ParseMode::Markdown,
                        config::OutputType::Html => types::request::ParseMode::Html,
                        config::OutputType::Json => unreachable!(),
                    };
                    work = bot.request::<_, serde_json::Value>(
                        "sendMessage",
                        &serde_json::to_value(
                            types::request::Message::new(msg.chat.id, out).parse_mode(
                                parse_mode,
                            ),
                        ).expect("cannot convert to Value"),
                    );
                }
                let work = work.and_then(|r| {
                    println!("{:?}", r);
                    future::ok(())
                }).map_err(|e| println!("error: {:?}", e));
                handle.spawn(work);
                future::ok(())
            },
        )
    });
    Box::new(res)
}

fn handle_inline_query(
    handle: Handle,
    bot: Bot,
    config: &config::Bot,
    query: serde_json::Value,
) -> Box<Future<Item = (), Error = errors::Error>> {
    let original = query.clone();
    let query: types::response::InlineQuery =
        serde_json::from_value(query).expect("malformed message");
    let offset = query.offset.parse::<i32>().unwrap_or(0);
    let cmd = config
        .commands
        .iter()
        .filter(|c| {
            c.allowed(query.from.id) && c.regex.is_match(&query.query) &&
                c.mode == config::Mode::Inline
        })
        .nth(0);
    let cmd = match cmd {
        Some(c) => c.clone(),
        None => return Box::new(future::ok(())),
    };
    let args: Vec<String> = {
        let captures = cmd.regex.captures(&query.query).expect(
            "we already checked for match",
        );
        cmd.args
            .iter()
            .map(|a| {
                //Ugly hack part 1
                let a = a.replace("${offset}", "$${offset}");
                let mut buf = String::new();
                captures.expand(&a, &mut buf);
                //Ugly hack part 2
                buf.replace("$${offset}", "${offset}")
            })
            .collect()
    };
    let query_text = query.query.clone();
    let h = handle.clone();
    let batch_size = 10;
    let cmd_clone = cmd.clone();
    let res = future::loop_fn(
        (0, Vec::new()),
        move |(iter, mut answers)| -> Box<Future<Item = future::Loop<_, _>, Error = errors::Error>> {
            if iter == batch_size - 1 {
                return Box::new(future::ok(future::Loop::Break((iter, answers))));
            }
            let args: Vec<_> = args.iter()
                .map(|a| a.replace("${offset}", &(offset + iter).to_string()))
                .collect();
            let child = Command::new(&cmd.executable)
                .args(&args)
                .stdin(Stdio::piped())
                .stdout(Stdio::piped())
                .spawn_async(&h);
            let mut child = match child {
                Ok(c) => c,
                Err(e) => return Box::new(future::err(e.into())),
            };
            let stdin = child.stdin().take().expect("no stdin for the process");
            let res = match cmd.input {
                config::InputType::Text => io::write_all(stdin, query_text.clone().into_bytes()),
                config::InputType::Json => {
                    io::write_all(stdin, serde_json::to_vec(&original).expect("cannot serialize"))
                }
            };
            Box::new(res.map_err(|e| e.into()).and_then(move |_| {
                child.wait_with_output().map_err(|e| e.into()).and_then(
                    move |out| {
                        let out = String::from_utf8(out.stdout).expect("encoding is not utf8");
                        answers.push(out);
                        Ok(future::Loop::Continue((iter + 1, answers)))
                    },
                )
            }))
        },
    );
    let res = res.and_then(move |(_, mut results)| {
        results.retain(|a| !a.is_empty());
        let new_offset = offset + results.len() as i32;
        let results: Vec<serde_json::Value> = {
            if cmd_clone.output == config::OutputType::Json {
                let mut new_results = Vec::new();
                let mut i = 0;
                for r in results {
                    let json: Result<serde_json::Value, _> = serde_json::from_str(&r);
                    let r = match json {
                        Ok(mut j) => {
                            match j.as_object_mut() {
                                Some(mut o) => {
                                    o.insert("id".to_owned(), json!((offset + i).to_string()))
                                }
                                None => {
                                    return future::err(
                                        errors::ErrorKind::Output(
                                            cmd_clone.executable,
                                            "Expected a json object",
                                        ).into(),
                                    )
                                }
                            };
                            j
                        }
                        Err(e) => return future::err(e.into()),
                    };
                    new_results.push(r);
                    i += 1;
                }
                new_results
            } else {
                results
                    .into_iter()
                    .enumerate()
                    .map(|(i, a)| {
                        let i = i as i32;
                        serde_json::to_value(types::request::InlineQueryResult::article(
                            (offset + i).to_string(),
                            a.clone(),
                            a.clone(),
                        )).expect("cannot convert to Value")
                    })
                    .collect()
            }
        };
        let answer =
            types::request::AnswerInlineQuery::new(query.id, results, new_offset.to_string());
        let work = bot.request::<_, serde_json::Value>(
            "answerInlineQuery",
            &serde_json::to_value(answer).expect("cannot converto to Value"),
        );
        let work = work.and_then(|r| {
            println!("{:?}", r);
            future::ok(())
        }).map_err(|e| println!("error: {:?}", e));
        handle.spawn(work);
        future::ok(())
    });
    Box::new(res)
}
fn main() {
    let opt = Opt::from_args();
    let config = config::get_all(&opt.config_dir).expect("Wrong path for config dir");

    let mut event_loop = reactor::Core::new().expect("cannot create reactor");
    let handle = event_loop.handle();

    let factory = BotFactory::new(handle.clone());
    let work = future::join_all(config.into_iter().map(|config_bot| {
        let handle = handle.clone();
        let (bot, updates) = factory.new_bot(&config_bot.token);
        updates
            .from_err()
            .and_then(move |update| {
                println!("{:?}", update);
                match update {
                    Update::Message(msg) => {
                        handle_message(handle.clone(), bot.clone(), &config_bot, msg)
                    }
                    Update::InlineQuery(query) => {
                        handle_inline_query(handle.clone(), bot.clone(), &config_bot, query)
                    }
                    _ => Box::new(future::ok(())),

                }
            })
            .or_else(|e| {
                println!("Error: {:?}", e);
                future::ok::<_, ()>(())
            })
            .for_each(|_| future::ok(()))
    }));
    event_loop.run(work).expect("Unexpected failure");
}
<|endoftext|>


<commit_before>extern crate sdl2;

use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::rect::Point;
use sdl2::render::Renderer;
use std::cmp::Ordering;

trait TinyRenderer {
    fn pixel(&mut self, Point, Color);
    fn line(&mut self, Point, Point, Color);
}

impl<'a> TinyRenderer for Renderer<'a> {
    fn pixel(&mut self, p: Point, c: Color) {
        let current_color = self.draw_color();

        self.set_draw_color(c);
        self.draw_point(p).unwrap();
        self.set_draw_color(current_color);
    }

    fn line(&mut self, start: Point, end: Point, c: Color) {
        let current_color = self.draw_color();
        self.set_draw_color(c);

        let mut x0 = start.x();
        let mut y0 = start.y();
        let mut x1 = end.x();
        let mut y1 = end.y();

        let steep = (y1 - y0).abs() > (x1 - x0).abs();

        if steep {
            std::mem::swap(&mut x0, &mut y0);
            std::mem::swap(&mut x1, &mut y1);
        }

        let reverse = x0 > x1;

        if reverse {
            std::mem::swap(&mut x0, &mut x1);
            std::mem::swap(&mut y0, &mut y1);
        }

        let dx = x1 - x0;
        let dy = (y1 - y0).abs();

        let mut err = dx / 2;
        let mut y = y0;
        let ystep = match y0.cmp(&y1) {
            Ordering::Less => 1,
            _ => -1
        };

        for x in x0..x1 {
            if steep {
                self.draw_point(Point::new(y, x)).unwrap();
            } else {
                self.draw_point(Point::new(x, y)).unwrap();
            }

            err -= dy;

            if err < 0 {
                y += ystep;
                err += dx;
            }
        }

        self.set_draw_color(current_color);
    }
}

fn main() {
    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("tinyrenderer-rs", 800, 600)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    renderer.set_draw_color(Color::RGB(0, 0, 0));
    renderer.clear();

    //Draw pixel
    renderer.pixel(Point::new(200, 200), Color::RGB(0, 255, 0));
    renderer.line(Point::new(250, 250), Point::new(385, 460), Color::RGB(0, 0, 255));
    renderer.line(Point::new(250, 250), Point::new(91, 0), Color::RGB(0, 255, 255));
    renderer.line(Point::new(250, 250), Point::new(385, 0), Color::RGB(255, 0, 255));

    renderer.present();

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'running
                },
                _ => {}
            }
        }
        //code here
    }
}
<commit_msg>Drawing wireframe model && using assimp lib to parse model<commit_after>extern crate sdl2;
extern crate assimp;

use assimp::Importer;
use sdl2::pixels::Color;
use sdl2::event::Event;
use sdl2::keyboard::Keycode;
use sdl2::rect::Point;
use sdl2::render::Renderer;
use std::cmp::Ordering;

use assimp::Vector3D;

trait TinyRenderer {
    fn pixel(&mut self, Point, Color);
    fn line(&mut self, Point, Point, Color);
}

impl<'a> TinyRenderer for Renderer<'a> {
    fn pixel(&mut self, p: Point, c: Color) {
        let current_color = self.draw_color();

        self.set_draw_color(c);
        self.draw_point(p).unwrap();
        self.set_draw_color(current_color);
    }

    fn line(&mut self, start: Point, end: Point, c: Color) {
        let current_color = self.draw_color();
        self.set_draw_color(c);

        let mut x0 = start.x();
        let mut y0 = start.y();
        let mut x1 = end.x();
        let mut y1 = end.y();

        let steep = (y1 - y0).abs() > (x1 - x0).abs();

        if steep {
            std::mem::swap(&mut x0, &mut y0);
            std::mem::swap(&mut x1, &mut y1);
        }

        let reverse = x0 > x1;

        if reverse {
            std::mem::swap(&mut x0, &mut x1);
            std::mem::swap(&mut y0, &mut y1);
        }

        let dx = x1 - x0;
        let dy = (y1 - y0).abs();

        let mut err = dx / 2;
        let mut y = y0;
        let ystep = match y0.cmp(&y1) {
            Ordering::Less => 1,
            _ => -1
        };

        for x in x0..x1 {
            if steep {
                self.draw_point(Point::new(y, x)).unwrap();
            } else {
                self.draw_point(Point::new(x, y)).unwrap();
            }

            err -= dy;

            if err < 0 {
                y += ystep;
                err += dx;
            }
        }

        self.set_draw_color(current_color);
    }
}

fn main() {
    let (width, height) = (800, 600);
    let importer = Importer::new();
    let scene = importer.read_file("resources/model.obj").unwrap();

    let sdl_context = sdl2::init().unwrap();
    let video_subsystem = sdl_context.video().unwrap();

    let window = video_subsystem.window("tinyrenderer-rs", width, height)
        .position_centered()
        .opengl()
        .build()
        .unwrap();

    let mut renderer = window.renderer().build().unwrap();

    renderer.set_draw_color(Color::RGB(0, 0, 0));
    renderer.clear();

    for mesh in scene.mesh_iter() {
        for face in mesh.face_iter() {
            for j in 0..3 {
                let v0 = match mesh.get_vertex(face[j]) {
                    Some(x) => x,
                    None => Vector3D::new(0., 0., 0.)
                };

                let v1 = match mesh.get_vertex(face[(j + 1) % 3]) {
                    Some(x) => x,
                    None => Vector3D::new(0., 0., 0.)
                };

                let x0 = width as i32 - ((v0.x + 1.) * width as f32 / 2.) as i32;
                let y0 = height as i32 - ((v0.y + 1.) * height as f32 / 2.) as i32;
                let x1 = width as i32 - ((v1.x + 1.) * width as f32 / 2.) as i32;
                let y1 = height as i32 - ((v1.y + 1.) * height as f32 / 2.) as i32;

                renderer.line(Point::new(x0, y0), Point::new(x1, y1), Color::RGB(255, 255, 255));
            }
        }
    }

    renderer.present();

    let mut event_pump = sdl_context.event_pump().unwrap();

    'running: loop {
        for event in event_pump.poll_iter() {
            match event {
                Event::Quit {..} | Event::KeyDown { keycode: Some(Keycode::Escape), .. } => {
                    break 'running
                },
                _ => {}
            }
        }
        //code here
    }
}
<|endoftext|>


<commit_before>extern crate clap;
extern crate qrcode;
extern crate termion;
extern crate image;

use clap::{App, Arg, SubCommand};
use qrcode::QrCode;
use termion::color;
use image::GrayImage;

fn main() {
    let matches = App::new("qrterm")
        .version("0.1")
        .author("Lukas R. <lukas@bootsmann-games.de>")
        .about("Generates and displays terminal friendly QR-Codes from input strings")
        .arg(Arg::with_name("type")
            .short("t")
            .long("type")
            .value_name("TYPE")
            .help("Sets if a special qr type should be used")
            .takes_value(true))
        .arg(Arg::with_name("INPUT")
            .help("The input string to use")
            .required(true)
            .index(1))
        .get_matches();

    let input = matches.value_of("INPUT").unwrap();

    //println!("Converting input string: {}", &input);

    let code = QrCode::new(input).unwrap();

    //let image: GrayImage = code.render().to_image();
    //image.save("/tmp/qrcode.png").unwrap();

    let bit_array = code.to_vec();

    let size = bit_array.len();
    let w = code.width();

    for i in 0..size {
        let item = bit_array[i];

        if item {
            print!("{}{}██", color::Fg(color::Black), color::Bg(color::Black)); //▜
        } else {
            print!("{}{}██", color::Fg(color::LightWhite), color::Bg(color::LightWhite));
        }

        if (i + 1) % w == 0 {
            println!("{}{}", color::Fg(color::Reset), color::Bg(color::Reset));
        }
    }
    //println!("");

    //println!("Hello, world!");
}
<commit_msg>added safe zone rendering<commit_after>extern crate clap;
extern crate qrcode;
extern crate termion;
extern crate image;

use clap::{App, Arg};
use qrcode::{QrCode, EcLevel};
use termion::color;
use termion::style;
use image::GrayImage;

/*TODO: add arguments for:
- safe zone rendering
- ec level
- size of qr code
- implement different types of qr payloads
*/
fn main() {
    let matches = App::new("qrterm")
        .version("0.1")
        .author("Lukas R. <lukas@bootsmann-games.de>")
        .about("Generates and displays terminal friendly QR-Codes from input strings")
        .arg(Arg::with_name("type")
            .short("t")
            .long("type")
            .value_name("TYPE")
            .help("Sets if a special qr type should be used")
            .takes_value(true))
        .arg(Arg::with_name("INPUT")
            .help("The input string to use")
            .required(true)
            .index(1))
        .get_matches();

    let input = matches.value_of("INPUT").unwrap();

    let code = QrCode::with_error_correction_level(input, EcLevel::H).unwrap();

    let image: GrayImage = code.render().to_image();
    image.save("/tmp/qrcode.png").unwrap();

    let bit_array = code.to_vec();

    let w = code.width();
    let wide = w + 6;

    //Draw the first white safe zone
    for a in 1..(wide * 3) + 1 {
        print!("{}  ", color::Bg(color::LightWhite));
        if a % wide == 0 {
            println!("{}", color::Bg(color::Reset));
        }
    }

    for (i, item) in bit_array.iter().enumerate() {
        //left safe zone
        if i % w == 0 {
            print!("{}      ", color::Bg(color::LightWhite));
        }

        if *item {
            print!("{}  ", color::Bg(color::Black)); //█ ▜
        } else {
            print!("{}  ", color::Bg(color::LightWhite));
        }

        if (i + 1) % w == 0 {
            println!("{}      {}",
                     color::Bg(color::LightWhite),
                     color::Bg(color::Reset));
        }
    }

    //Draw the last white safe zone
    for a in 1..(wide * 3) + 1 {
        print!("{}  ", color::Bg(color::LightWhite));
        if a % wide == 0 {
            println!("{}", color::Bg(color::Reset));
        }
    }
}
<|endoftext|>


<commit_before>extern crate libc;
extern crate termios;

use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, winsize};
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::io::{self, Stdin, Stdout, Read, Error, ErrorKind, Write};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct RawMode {
    orig_term: Termios
}

struct Editor {
    _mode: RawMode,
    screenrows: u16,
    screencols: u16,
    stdin: Stdin,
    stdout: Stdout,
}

impl RawMode {
    fn enable_raw_mode() -> io::Result<RawMode> {
        let mut term = Termios::from_fd(STDIN_FILENO)?;
        let mode = RawMode { orig_term: term.clone() };

        term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        term.c_oflag &= !OPOST;
        term.c_cflag |= CS8;
        term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
        term.c_cc[VMIN] = 0;
        term.c_cc[VTIME] = 1;

        tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
        Ok(mode)
    }
}

impl Drop for RawMode {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

fn editor_read_key(stdin: &mut Stdin) -> u8 {
    let mut buf = [0; 1];
    loop {
        let n = stdin.read(&mut buf)
                 .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
                 .expect("read");
        if n == 1 {
            return buf[0];
        }
    }
}

fn get_window_size() -> io::Result<(u16, u16)> {
    let ws = winsize { ws_col: 0, ws_row: 0, ws_xpixel: 0, ws_ypixel: 0 };
    unsafe {
        if libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0 {
            return Err(Error::new(ErrorKind::Other, "get_window_size: ioctl failed"))
        }
    }
    Ok((ws.ws_row, ws.ws_col))
}

impl Editor {
    fn new() -> io::Result<Editor> {
        let mode = RawMode::enable_raw_mode()?;
        let (rows, cols) = get_window_size()?;
        let stdin = io::stdin();
        let stdout = io::stdout();
        Ok(Editor { _mode: mode, screenrows: rows, screencols: cols, stdin: stdin, stdout: stdout })
    }

    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }

    fn draw_rows(&mut self) -> io::Result<()> {
        for y in 0..(self.screenrows) {
            if y == self.screenrows / 3 {
                let mut msg = format!("Kilo-rs editor -- version {}", VERSION);
                msg.truncate(self.screencols as usize);
                let padding = (self.screencols - msg.len() as u16) / 2;
                if padding > 0 {
                    self.write(b"~")?;
                    for _ in 1..padding {
                        self.write(b" ")?;
                    }
                }
                self.write(msg.as_bytes())?;
            } else {
                self.write(b"~")?;
            }

            self.write(b"\x1b[K")?;
            if y < self.screenrows - 1 {
                self.write(b"\r\n")?;
            }
        }
        Ok(())
    }

    fn refresh_screen(&mut self) -> io::Result<()> {
        self.write(b"\x1b[?25l")?;
        self.write(b"\x1b[H")?;

        self.draw_rows()?;

        self.write(b"\x1b[H")?;
        self.write(b"\x1b[?25h")?;
        self.flush()
    }

    fn process_keypress(&mut self) -> bool {
        let c = editor_read_key(&mut self.stdin);

        match c {
            k if k == ctrl_key!(b'q') => {
                false
            },
            _ => true
        }
    }
}

impl Drop for Editor {
    fn drop(&mut self) {
        // clear screen
        self.stdout.write(b"\x1b[2J").unwrap();
        self.stdout.write(b"\x1b[H").unwrap();
        self.stdout.flush().unwrap();
    }
}

fn main() {
    let mut editor = Editor::new().unwrap();

    editor.refresh_screen().unwrap();
    while editor.process_keypress() {
        editor.refresh_screen().unwrap();
    }
}
<commit_msg>Move the cursor<commit_after>extern crate libc;
extern crate termios;

use libc::{STDIN_FILENO, STDOUT_FILENO, TIOCGWINSZ, winsize};
use termios::{Termios, tcsetattr, TCSAFLUSH, VMIN, VTIME};
use termios::{BRKINT, ICRNL, INPCK, ISTRIP, IXON};
use termios::{OPOST, CS8};
use termios::{ECHO, ICANON, IEXTEN, ISIG};

use std::io::{self, Stdin, Stdout, Read, Error, ErrorKind, Write};

const VERSION: &'static str = env!("CARGO_PKG_VERSION");

macro_rules! ctrl_key {
    ($k:expr) => ($k & 0x1f);
}

struct RawMode {
    orig_term: Termios
}

struct Editor {
    _mode: RawMode,
    cx: u16,
    cy: u16,
    screenrows: u16,
    screencols: u16,
    stdin: Stdin,
    stdout: Stdout,
}

impl RawMode {
    fn enable_raw_mode() -> io::Result<RawMode> {
        let mut term = Termios::from_fd(STDIN_FILENO)?;
        let mode = RawMode { orig_term: term.clone() };

        term.c_iflag &= !(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
        term.c_oflag &= !OPOST;
        term.c_cflag |= CS8;
        term.c_lflag &= !(ECHO | ICANON | IEXTEN | ISIG);
        term.c_cc[VMIN] = 0;
        term.c_cc[VTIME] = 1;

        tcsetattr(STDIN_FILENO, TCSAFLUSH, &term)?;
        Ok(mode)
    }
}

impl Drop for RawMode {
    fn drop(&mut self) {
        tcsetattr(STDIN_FILENO, TCSAFLUSH, &self.orig_term).unwrap()
    }
}

fn editor_read_key(stdin: &mut Stdin) -> u8 {
    let mut buf = [0; 1];
    loop {
        let n = stdin.read(&mut buf)
                 .or_else(|e| if e.kind() == ErrorKind::WouldBlock { Ok(0) } else { Err(e) })
                 .expect("read");
        if n == 1 {
            return buf[0];
        }
    }
}

fn get_window_size() -> io::Result<(u16, u16)> {
    let ws = winsize { ws_col: 0, ws_row: 0, ws_xpixel: 0, ws_ypixel: 0 };
    unsafe {
        if libc::ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0 {
            return Err(Error::new(ErrorKind::Other, "get_window_size: ioctl failed"))
        }
    }
    Ok((ws.ws_row, ws.ws_col))
}

impl Editor {
    fn new() -> io::Result<Editor> {
        let mode = RawMode::enable_raw_mode()?;
        let (rows, cols) = get_window_size()?;
        let stdin = io::stdin();
        let stdout = io::stdout();
        Ok(Editor {
            _mode: mode,
            cx: 0,
            cy: 0,
            screenrows: rows,
            screencols: cols,
            stdin: stdin,
            stdout: stdout
        })
    }

    fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
        self.stdout.write(buf)
    }

    fn flush(&mut self) -> io::Result<()> {
        self.stdout.flush()
    }

    fn draw_rows(&mut self) -> io::Result<()> {
        for y in 0..(self.screenrows) {
            if y == self.screenrows / 3 {
                let mut msg = format!("Kilo-rs editor -- version {}", VERSION);
                msg.truncate(self.screencols as usize);
                let padding = (self.screencols - msg.len() as u16) / 2;
                if padding > 0 {
                    self.write(b"~")?;
                    for _ in 1..padding {
                        self.write(b" ")?;
                    }
                }
                self.write(msg.as_bytes())?;
            } else {
                self.write(b"~")?;
            }

            self.write(b"\x1b[K")?;
            if y < self.screenrows - 1 {
                self.write(b"\r\n")?;
            }
        }
        Ok(())
    }

    fn refresh_screen(&mut self) -> io::Result<()> {
        self.write(b"\x1b[?25l")?;
        self.write(b"\x1b[H")?;

        self.draw_rows()?;

        let move_cursor = format!("\x1b[{};{}H", self.cy + 1, self.cx + 1).into_bytes();
        self.write(&move_cursor)?;

        self.write(b"\x1b[?25h")?;
        self.flush()
    }

    fn move_cursor(&mut self, c: u8) {
        match c {
            b'w' => {
                self.cy -= 1;
            },
            b's' => {
                self.cy += 1;
            },
            b'a' => {
                self.cx -= 1;
            },
            b'd' => {
                self.cx += 1;
            },
            _ => {},
        }
    }

    fn process_keypress(&mut self) -> bool {
        let c = editor_read_key(&mut self.stdin);

        match c {
            k if k == ctrl_key!(b'q') => {
                false
            },
            b'w' | b's' | b'a' |  b'd' => {
                self.move_cursor(c);
                true
            },
            _ => true
        }
    }
}

impl Drop for Editor {
    fn drop(&mut self) {
        // clear screen
        self.stdout.write(b"\x1b[2J").unwrap();
        self.stdout.write(b"\x1b[H").unwrap();
        self.stdout.flush().unwrap();
    }
}

fn main() {
    let mut editor = Editor::new().unwrap();

    editor.refresh_screen().unwrap();
    while editor.process_keypress() {
        editor.refresh_screen().unwrap();
    }
}
<|endoftext|>


<commit_before>#![feature(io, slice_patterns, vec_resize)]
use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;

#[derive(Debug)]
enum ParserResult {
    Something(Op),
    Nothing,
    EOF
}

use ParserResult::*;

#[derive(Debug)]
enum Op {
    Add(u8),
    Mov(isize),
    In,
    Out,
    Loop(OpStream),

    // extra optimized ops
    Transfer(u8, Vec<(isize, u8)>)
}

use Op::*;

#[derive(Debug)]
struct OpStream {
    ops: Vec<Op>
}

impl OpStream {
    fn add(&mut self, op: Op) {
        self.ops.push(op);
    }

    fn optimize(&mut self) {
        let mut i = 0;
        while i < self.ops.len() {
            match &self.ops[i..] {
                [Add(a), Add(b), ..] => {
                    self.ops[i] = Add(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Mov(a), Mov(b), ..] => {
                    self.ops[i] = Mov(a + b);
                    self.ops.remove(i + 1);
                }
                [Add(0), ..] | [Mov(0), ..] => {
                    self.ops.remove(i);
                }
                [Loop(_), ..] => {
                    let mut maybe_new_op = None;

                    if let &mut Loop(ref mut stream) = &mut self.ops[i] {
                        stream.optimize();
                        maybe_new_op = stream.find_alternative();
                    }

                    if let Some(new_op) = maybe_new_op {
                        self.ops[i] = new_op;
                    }

                    i += 1
                }
                _ => i += 1
            }
        }
    }

    fn find_alternative(&self) -> Option<Op> {
        let mut map: BTreeMap<isize, u8> = BTreeMap::new();
        let mut rel_index = 0;

        for op in &self.ops[..] {
            match op {
                &Add(x) => {
                    let new_val = map.get(&rel_index).unwrap_or(&0).wrapping_add(x);
                    map.insert(rel_index, new_val);
                }
                &Mov(x) => {
                    rel_index += x;
                }
                _ => {
                    return None;
                }
            }
        }

        if rel_index != 0 {
            return None;
        }

        match map.remove(&0) {
            Some(summand) => Some(Transfer(summand, map.into_iter().collect())),
            None => None
        }
    }

    fn get(&self) -> &[Op] {
        &self.ops[..]
    }

    fn new() -> OpStream {
        OpStream { ops: Vec::new() }
    }
}

static ZERO: u8 = 0;

struct State {
    index: usize,
    memory: Vec<u8>
}

impl State {
    fn new() -> State {
        State { index: 0, memory: vec![] }
    }

    fn rel_index(&self, relative: isize) -> usize {
        (self.index as isize + relative) as usize
    }

    fn step(&mut self, op: &Op) -> bool {
        match op {
            &Add(i) => {
                self[0] = self[0].wrapping_add(i);
            }
            &Mov(n) => {
                self.index = self.rel_index(n);
            }
            &In => {
                let mut c = [0u8];
                if io::stdin().read(&mut c).unwrap() == 0 {
                    return false;
                }
                self[0] = c[0];
            }
            &Out => {
                io::stdout().write(&[self[0]]).unwrap();
            }
            &Loop(ref ops) => {
                while self[0] != 0 {
                    if !self.run(ops.get()) {
                        return false;
                    }
                }
            }
            &Transfer(d, ref map) => {
                if self[0] == 0 {
                    return true
                }

                let mut _v = self[0];
                let mut _n = 0;

                while _v != 0 {
                    _v = _v.wrapping_add(d);
                    _n += 1
                }

                self[0] = 0;
                for &(k, v) in &map[..] {
                    self[k] = self[k].wrapping_add(v.wrapping_mul(_n));
                }
            }
        }
        return true;
    }

    fn run(&mut self, ops: &[Op]) -> bool {
        for op in ops {
            if !self.step(op) {
                return false;
            }
        }
        return true;
    }
}

impl std::ops::Index<isize> for State {
    type Output = u8;
    fn index(&self, _index: isize) -> &u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            &ZERO
        } else {
            &self.memory[idx]
        }
    }
}

impl std::ops::IndexMut<isize> for State {
    fn index_mut(&mut self, _index: isize) -> &mut u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            self.memory.resize(idx * 2 + 1, 0);
        }
        &mut self.memory[idx]
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        match reader(&Path::new(filename)) {
            Ok(reader) => {
                let mut chars = reader.chars().peekable();

                let mut opstream = OpStream::new();
                loop {
                    match parse(&mut chars) {
                        Something(op) => opstream.add(op),
                        EOF => break,
                        _ => {}
                    }
                }

                opstream.optimize();

                State::new().run(opstream.get());
            }
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while processing {}: {}", filename, e).unwrap();
            }
        }
    }
}

fn parse<T: io::Read>(mut chars: &mut std::iter::Peekable<io::Chars<T>>) -> ParserResult {
    match chars.next() {
        Some(Ok(c)) => match c {
            '+' => Something(Add(0x01)),
            '-' => Something(Add(0xff)),
            '<' => Something(Mov(-1)),
            '>' => Something(Mov(1)),
            ',' => Something(In),
            '.' => Something(Out),
            '[' => {
                let mut childstream = OpStream::new();
                while match chars.peek() {
                    Some(&Ok(c)) => c != ']',
                    _ => panic!("Unterminated loop")
                } {
                    if let Something(op) = parse(&mut chars) {
                        childstream.add(op)
                    }
                }
                chars.next();
                Something(Loop(childstream))
            },
            _ => Nothing  // other characters
        },
        _ => EOF  // XXX: really?
    }
}

fn reader(path: &Path) -> Result<io::BufReader<fs::File>, io::Error> {
    fs::File::open(path).map(|f| io::BufReader::new(f))
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use super::{parse, ParserResult, State, OpStream};
    use super::Op::*;

    macro_rules! assert_let(
        ($test:pat, $value:expr, $then:block) => (
            if let $test = $value {
                $then
            } else {
                panic!("{:?} doesn't match {}", $value, stringify!($test))
            }
        );
        ($test:pat, $value:expr) => (
            assert_let!($test, $value, {})
        );
    );

    #[test]
    fn test_state_index() {
        let mut state = State { index: 0, memory: vec![23, 0, 0, 0, 0, 42] };
        assert_eq!(23, state[0]);
        state.index = 5;
        assert_eq!(42, state[0]);
    }

    #[test]
    fn test_state_index_mut() {
        let mut state = State::new();
        state[0] = 23;
        assert_eq!(23, state.memory[state.index]);
        state.index = 5;
        state[0] = 42;
        assert_eq!(42, state.memory[state.index]);
    }

    #[test]
    fn test_state_step_add() {
        let mut state = State::new();
        state.step(&Add(23));
        assert_eq!(23, state[0]);
        state.step(&Add(42));
        assert_eq!(65, state[0]);
        state.step(&Add(190));
        assert_eq!(255, state[0]);
    }

    #[test]
    fn test_state_step_mov() {
        let mut state = State::new();
        state.step(&Mov(1));
        assert_eq!(1, state.index);
        state.step(&Mov(42));
        assert_eq!(43, state.index);
        state.step(&Mov(-1));
        assert_eq!(42, state.index);
    }

    #[test]
    fn test_state_step_loop() {
        let mut state = State::new();
        state[0] = 23;
        state.step(&Loop(OpStream { ops: vec![Add(1)] }));
        assert_eq!(0, state[0]);
    }

    #[test]
    fn test_state_step_transfer() {
        let mut state = State::new();
        state[0] = 15;
        state[1] = 7;
        state.step(&Transfer(5, vec![(1, 2)]));
        assert_eq!(0, state[0]);
        assert_eq!(1, state[1]);
    }

    #[test]
    fn test_opstream_new_empty() {
        let opstream = OpStream::new();
        assert_eq!(0, opstream.ops.len());
    }

    #[test]
    fn test_opstream_optimize() {
        let mut opstream = OpStream::new();
        opstream.add(Mov(1));
        opstream.add(Mov(1));
        opstream.add(Add(0x01));
        opstream.add(Add(0xff));
        opstream.add(Add(0xff));
        opstream.add(Mov(1));
        opstream.add(Mov(-1));
        let mut opstream2 = OpStream::new();
        opstream2.add(Mov(2));
        opstream2.add(Mov(3));
        opstream.add(Loop(opstream2));
        opstream.optimize();

        assert_let!([Mov(2), Add(0xff), Loop(ref s2)], &opstream.ops[..], {
            assert_let!([Mov(5)], &s2.ops[..]);
        });
    }

    #[test]
    fn test_parse() {
        let input = b"+-[+.,]+";
        let mut chars = io::BufReader::new(&input[..]).chars().peekable();
        assert_let!(ParserResult::Something(Add(0x01)), parse(&mut chars));
        assert_let!(ParserResult::Something(Add(0xff)), parse(&mut chars));
        assert_let!(ParserResult::Something(Loop(loop_op)), parse(&mut chars), {
            assert_let!([Add(1), Out, In], &loop_op.ops[..]);
        });
        assert_let!(ParserResult::Something(Add(1)), parse(&mut chars));
    }
}
<commit_msg>feat: efficiently stall when the program stalls<commit_after>#![feature(io, slice_patterns, vec_resize)]
use std::collections::BTreeMap;
use std::fs;
use std::io;
use std::io::{Read, Write};
use std::path::Path;
use std::thread;

#[derive(Debug)]
enum ParserResult {
    Something(Op),
    Nothing,
    EOF
}

use ParserResult::*;

#[derive(Debug)]
enum Op {
    Add(u8),
    Mov(isize),
    In,
    Out,
    Loop(OpStream),

    // extra optimized ops
    Transfer(u8, Vec<(isize, u8)>)
}

use Op::*;

#[derive(Debug)]
struct OpStream {
    ops: Vec<Op>
}

impl OpStream {
    fn add(&mut self, op: Op) {
        self.ops.push(op);
    }

    fn optimize(&mut self) {
        let mut i = 0;
        while i < self.ops.len() {
            match &self.ops[i..] {
                [Add(a), Add(b), ..] => {
                    self.ops[i] = Add(a.wrapping_add(b));
                    self.ops.remove(i + 1);
                }
                [Mov(a), Mov(b), ..] => {
                    self.ops[i] = Mov(a + b);
                    self.ops.remove(i + 1);
                }
                [Add(0), ..] | [Mov(0), ..] => {
                    self.ops.remove(i);
                }
                [Loop(_), ..] => {
                    let mut maybe_new_op = None;

                    if let &mut Loop(ref mut stream) = &mut self.ops[i] {
                        stream.optimize();
                        maybe_new_op = stream.find_alternative();
                    }

                    if let Some(new_op) = maybe_new_op {
                        self.ops[i] = new_op;
                    }

                    i += 1
                }
                _ => i += 1
            }
        }
    }

    fn find_alternative(&self) -> Option<Op> {
        let mut map: BTreeMap<isize, u8> = BTreeMap::new();
        let mut rel_index = 0;

        for op in &self.ops[..] {
            match op {
                &Add(x) => {
                    let new_val = map.get(&rel_index).unwrap_or(&0).wrapping_add(x);
                    map.insert(rel_index, new_val);
                }
                &Mov(x) => {
                    rel_index += x;
                }
                _ => {
                    return None;
                }
            }
        }

        if rel_index != 0 {
            return None;
        }

        match map.remove(&0) {
            Some(summand) => Some(Transfer(summand, map.into_iter().collect())),
            None => None
        }
    }

    fn get(&self) -> &[Op] {
        &self.ops[..]
    }

    fn new() -> OpStream {
        OpStream { ops: Vec::new() }
    }
}

static ZERO: u8 = 0;

struct State {
    index: usize,
    memory: Vec<u8>
}

impl State {
    fn new() -> State {
        State { index: 0, memory: vec![] }
    }

    fn rel_index(&self, relative: isize) -> usize {
        (self.index as isize + relative) as usize
    }

    fn step(&mut self, op: &Op) -> bool {
        match op {
            &Add(i) => {
                self[0] = self[0].wrapping_add(i);
            }
            &Mov(n) => {
                self.index = self.rel_index(n);
            }
            &In => {
                let mut c = [0u8];
                if io::stdin().read(&mut c).unwrap() == 0 {
                    return false;
                }
                self[0] = c[0];
            }
            &Out => {
                io::stdout().write(&[self[0]]).unwrap();
            }
            &Loop(ref ops) => {
                while self[0] != 0 {
                    if !self.run(ops.get()) {
                        return false;
                    }
                }
            }
            &Transfer(d, ref map) => {
                if self[0] == 0 {
                    return true
                }

                let mut _v = self[0];
                let mut _n = 0;

                while _v != 0 {
                    _v = _v.wrapping_add(d);
                    if _v == self[0] {
                        // stalled: the current transfer will never complete
                        loop {
                            thread::park()
                        }
                    }
                    _n += 1
                }

                self[0] = 0;
                for &(k, v) in &map[..] {
                    self[k] = self[k].wrapping_add(v.wrapping_mul(_n));
                }
            }
        }
        return true;
    }

    fn run(&mut self, ops: &[Op]) -> bool {
        for op in ops {
            if !self.step(op) {
                return false;
            }
        }
        return true;
    }
}

impl std::ops::Index<isize> for State {
    type Output = u8;
    fn index(&self, _index: isize) -> &u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            &ZERO
        } else {
            &self.memory[idx]
        }
    }
}

impl std::ops::IndexMut<isize> for State {
    fn index_mut(&mut self, _index: isize) -> &mut u8 {
        let idx = self.rel_index(_index);
        if idx >= self.memory.len() {
            self.memory.resize(idx * 2 + 1, 0);
        }
        &mut self.memory[idx]
    }
}

fn main() {
    let filenames: Vec<String> = std::iter::FromIterator::from_iter(std::env::args());

    for filename in &filenames[1..] {
        match reader(&Path::new(filename)) {
            Ok(reader) => {
                let mut chars = reader.chars().peekable();

                let mut opstream = OpStream::new();
                loop {
                    match parse(&mut chars) {
                        Something(op) => opstream.add(op),
                        EOF => break,
                        _ => {}
                    }
                }

                opstream.optimize();

                State::new().run(opstream.get());
            }
            Err(e) => {
                writeln!(&mut io::stderr(), "Error while processing {}: {}", filename, e).unwrap();
            }
        }
    }
}

fn parse<T: io::Read>(mut chars: &mut std::iter::Peekable<io::Chars<T>>) -> ParserResult {
    match chars.next() {
        Some(Ok(c)) => match c {
            '+' => Something(Add(0x01)),
            '-' => Something(Add(0xff)),
            '<' => Something(Mov(-1)),
            '>' => Something(Mov(1)),
            ',' => Something(In),
            '.' => Something(Out),
            '[' => {
                let mut childstream = OpStream::new();
                while match chars.peek() {
                    Some(&Ok(c)) => c != ']',
                    _ => panic!("Unterminated loop")
                } {
                    if let Something(op) = parse(&mut chars) {
                        childstream.add(op)
                    }
                }
                chars.next();
                Something(Loop(childstream))
            },
            _ => Nothing  // other characters
        },
        _ => EOF  // XXX: really?
    }
}

fn reader(path: &Path) -> Result<io::BufReader<fs::File>, io::Error> {
    fs::File::open(path).map(|f| io::BufReader::new(f))
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::io::Read;
    use super::{parse, ParserResult, State, OpStream};
    use super::Op::*;

    macro_rules! assert_let(
        ($test:pat, $value:expr, $then:block) => (
            if let $test = $value {
                $then
            } else {
                panic!("{:?} doesn't match {}", $value, stringify!($test))
            }
        );
        ($test:pat, $value:expr) => (
            assert_let!($test, $value, {})
        );
    );

    #[test]
    fn test_state_index() {
        let mut state = State { index: 0, memory: vec![23, 0, 0, 0, 0, 42] };
        assert_eq!(23, state[0]);
        state.index = 5;
        assert_eq!(42, state[0]);
    }

    #[test]
    fn test_state_index_mut() {
        let mut state = State::new();
        state[0] = 23;
        assert_eq!(23, state.memory[state.index]);
        state.index = 5;
        state[0] = 42;
        assert_eq!(42, state.memory[state.index]);
    }

    #[test]
    fn test_state_step_add() {
        let mut state = State::new();
        state.step(&Add(23));
        assert_eq!(23, state[0]);
        state.step(&Add(42));
        assert_eq!(65, state[0]);
        state.step(&Add(190));
        assert_eq!(255, state[0]);
    }

    #[test]
    fn test_state_step_mov() {
        let mut state = State::new();
        state.step(&Mov(1));
        assert_eq!(1, state.index);
        state.step(&Mov(42));
        assert_eq!(43, state.index);
        state.step(&Mov(-1));
        assert_eq!(42, state.index);
    }

    #[test]
    fn test_state_step_loop() {
        let mut state = State::new();
        state[0] = 23;
        state.step(&Loop(OpStream { ops: vec![Add(1)] }));
        assert_eq!(0, state[0]);
    }

    #[test]
    fn test_state_step_transfer() {
        let mut state = State::new();
        state[0] = 15;
        state[1] = 7;
        state.step(&Transfer(5, vec![(1, 2)]));
        assert_eq!(0, state[0]);
        assert_eq!(1, state[1]);
    }

    #[test]
    fn test_opstream_new_empty() {
        let opstream = OpStream::new();
        assert_eq!(0, opstream.ops.len());
    }

    #[test]
    fn test_opstream_optimize() {
        let mut opstream = OpStream::new();
        opstream.add(Mov(1));
        opstream.add(Mov(1));
        opstream.add(Add(0x01));
        opstream.add(Add(0xff));
        opstream.add(Add(0xff));
        opstream.add(Mov(1));
        opstream.add(Mov(-1));
        let mut opstream2 = OpStream::new();
        opstream2.add(Mov(2));
        opstream2.add(Mov(3));
        opstream.add(Loop(opstream2));
        opstream.optimize();

        assert_let!([Mov(2), Add(0xff), Loop(ref s2)], &opstream.ops[..], {
            assert_let!([Mov(5)], &s2.ops[..]);
        });
    }

    #[test]
    fn test_parse() {
        let input = b"+-[+.,]+";
        let mut chars = io::BufReader::new(&input[..]).chars().peekable();
        assert_let!(ParserResult::Something(Add(0x01)), parse(&mut chars));
        assert_let!(ParserResult::Something(Add(0xff)), parse(&mut chars));
        assert_let!(ParserResult::Something(Loop(loop_op)), parse(&mut chars), {
            assert_let!([Add(1), Out, In], &loop_op.ops[..]);
        });
        assert_let!(ParserResult::Something(Add(1)), parse(&mut chars));
    }
}
<|endoftext|>