<commit_before>extern crate poke_a_mango;
extern crate tabwriter;
extern crate cursive;

use std::cmp;
use std::path::Path;
use std::str::FromStr;
use std::process::exit;
use tabwriter::TabWriter;
use cursive::view::Selector;
use std::io::{Write, stderr};
use cursive::{ScreenId, Cursive};
use cursive::traits::Identifiable;
use cursive::views::{LinearLayout, TextView, Button};

fn main() {
    let result = actual_main();
    exit(result);
}

fn actual_main() -> i32 {
    if let Err(err) = result_main() {
        err.print_error(&mut stderr());
        err.exit_value()
    } else {
        0
    }
}

fn result_main() -> Result<(), poke_a_mango::Error> {
    let opts = poke_a_mango::Options::parse();

    let mut ui = Cursive::new();
    ui.set_fps(60);

    setup_ui((ui.add_screen(), ui.add_screen(), ui.add_screen(), ui.add_screen(), ui.add_screen(), ui.add_screen()),
             &mut ui,
             &opts.config_dir.1);

    ui.run();

    Ok(())
}

fn setup_ui(screens: (ScreenId, ScreenId, ScreenId, ScreenId, ScreenId, ScreenId), ui: &mut Cursive, p: &Path) {
    let (main_menu_screen, choose_difficulty_screen, highscores_screen, play_game_screen, game_over_screen, data_save_screen) = screens;

    ui.set_screen(choose_difficulty_screen);
    ui.add_layer(LinearLayout::vertical()
        .child(Button::new("Easy", move |ui| {
            ui.set_screen(data_save_screen);
            ui.add_layer(TextView::new(poke_a_mango::ops::Difficulty::Easy.numeric().to_string()).with_id("difficulty"));
            ui.set_screen(play_game_screen);
        }))
        .child(Button::new("Normal", move |ui| {
            ui.set_screen(data_save_screen);
            ui.add_layer(TextView::new(poke_a_mango::ops::Difficulty::Normal.numeric().to_string()).with_id("difficulty"));
            ui.set_screen(play_game_screen);
        }))
        .child(Button::new("Hard", move |ui| {
            ui.set_screen(data_save_screen);
            ui.add_layer(TextView::new(poke_a_mango::ops::Difficulty::Hard.numeric().to_string()).with_id("difficulty"));
            ui.set_screen(play_game_screen);
        }))
        .child(Button::new("Back", move |ui| ui.set_screen(main_menu_screen))));

    ui.set_screen(data_save_screen);
    ui.add_layer(TextView::new("").with_id("fruit"));
    ui.add_layer(TextView::new("0").with_id("score"));

    ui.set_screen(play_game_screen);
    ui.add_layer(LinearLayout::vertical()
        .child(TextView::new("Poke a mango!"))
        .child(TextView::new("Current fruit: Mango").with_id("mango_label"))
        .child(Button::new("Poke it!", move |ui| {
            let was_mango = mango_butan_press_common(ui, data_save_screen, play_game_screen);

            if was_mango {
                update_score_in_ui(ui, data_save_screen, play_game_screen, 1);
            } else {
                ui.set_screen(game_over_screen);
            }
        }))
        .child(Button::new("Ignore it", move |ui| {
            mango_butan_press_common(ui, data_save_screen, play_game_screen);
        })));

    ui.set_screen(game_over_screen);
    ui.add_layer(LinearLayout::vertical().child(TextView::new("Game over!")));

    ui.set_screen(main_menu_screen);
    ui.add_layer(LinearLayout::vertical()
        .child(Button::new("Start game", move |ui| ui.set_screen(choose_difficulty_screen)))
        .child(Button::new("Highscores", {
            let p = p.to_path_buf();
            move |ui| setup_leaderboard(highscores_screen, main_menu_screen, ui, &p)
        }))
        .child(Button::new("Exit", Cursive::quit)));
}

fn setup_leaderboard(screen: ScreenId, back_screen: ScreenId, ui: &mut Cursive, p: &Path) {
    ui.set_screen(screen);
    let screen = ui.screen_mut();
    screen.pop_layer();

    let ldrbrd = leaderboard_to_string_form(poke_a_mango::ops::Leader::read(&p.join("leaderboard.toml")).expect("Failed to load leaderboard"));
    let mut layout = LinearLayout::vertical();
    for ldr in ldrbrd.into_iter() {
        layout.add_child(TextView::new(ldr));
    }
    layout.add_child(Button::new("Back", move |ui| ui.set_screen(back_screen)));

    screen.add_layer(layout);
}

fn leaderboard_to_string_form(ldrbrd: Vec<poke_a_mango::ops::Leader>) -> Vec<String> {
    let mut tw = TabWriter::new(vec![]);

    let leader_n = cmp::min(ldrbrd.len(), 10);
    for ldr in ldrbrd.into_iter().take(leader_n) {
        writeln!(&mut tw, "{}\t{}", ldr.name, ldr.score).unwrap();
    }

    tw.flush().unwrap();
    String::from_utf8(tw.unwrap()).unwrap().lines().map(str::to_string).collect()
}

fn fruit_from_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId) -> Option<usize> {
    ui.set_screen(data);
    let fruit = {
        let ref mut fruit: &mut TextView = ui.find(&Selector::Id("fruit")).unwrap();
        match fruit.get_content() {
            "" => None,
            l => Some(usize::from_str(l).unwrap()),
        }
    };
    ui.set_screen(back);
    fruit
}

fn fruit_to_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId, new_fruit: Option<usize>) {
    ui.set_screen(data);
    {
        let ref mut fruit: &mut TextView = ui.find(&Selector::Id("fruit")).unwrap();
        fruit.set_content(new_fruit.as_ref().map(usize::to_string).unwrap_or(String::new()));
    }
    ui.set_screen(back);
}

fn difficulty_from_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId) -> poke_a_mango::ops::Difficulty {
    ui.set_screen(data);
    let difficulty = {
        let ref mut difficulty: &mut TextView = ui.find(&Selector::Id("difficulty")).unwrap();
        poke_a_mango::ops::Difficulty::from_numeric(u32::from_str(difficulty.get_content()).unwrap()).unwrap()
    };
    ui.set_screen(back);
    difficulty
}

fn update_score_in_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId, by: u64) -> u64 {
    ui.set_screen(data);
    let score = {
        let ref mut score: &mut TextView = ui.find(&Selector::Id("score")).unwrap();
        let score_n = u64::from_str(score.get_content()).unwrap() + by;
        score.set_content(score_n.to_string());
        score_n
    };
    ui.set_screen(back);
    score
}

fn mango_butan_press_common(ui: &mut Cursive, data: ScreenId, back: ScreenId) -> bool {
    let fruit = fruit_from_ui(ui, data, back);
    let difficulty = difficulty_from_ui(ui, data, back);

    let mut state = poke_a_mango::ops::GameState::Playing {
        difficulty: difficulty,
        score: 0,
        fruit: fruit,
    };
    poke_a_mango::ops::state::tick_mango(&mut state);
    if let poke_a_mango::ops::GameState::Playing { fruit, .. } = state {
        fruit_to_ui(ui, data, back, fruit);

        let ref mut mango_label: &mut TextView = ui.find(&Selector::Id("mango_label")).expect("mango_label");
        mango_label.set_content(format!("Current fruit: {}", poke_a_mango::util::fruit_name(&fruit)));
    }

    fruit.is_none()
}
<commit_msg>Add Game Over screen<commit_after>extern crate poke_a_mango;
extern crate tabwriter;
extern crate cursive;

use std::cmp;
use std::path::Path;
use std::str::FromStr;
use std::process::exit;
use tabwriter::TabWriter;
use cursive::view::Selector;
use std::io::{Write, stderr};
use cursive::{ScreenId, Cursive};
use cursive::traits::Identifiable;
use cursive::views::{LinearLayout, EditView, TextView, Button};

fn main() {
    let result = actual_main();
    exit(result);
}

fn actual_main() -> i32 {
    if let Err(err) = result_main() {
        err.print_error(&mut stderr());
        err.exit_value()
    } else {
        0
    }
}

fn result_main() -> Result<(), poke_a_mango::Error> {
    let opts = poke_a_mango::Options::parse();

    let mut ui = Cursive::new();
    ui.set_fps(60);

    setup_ui((ui.add_screen(), ui.add_screen(), ui.add_screen(), ui.add_screen(), ui.add_screen(), ui.add_screen()),
             &mut ui,
             &opts.config_dir.1);

    ui.run();

    Ok(())
}

fn setup_ui(screens: (ScreenId, ScreenId, ScreenId, ScreenId, ScreenId, ScreenId), ui: &mut Cursive, p: &Path) {
    let (main_menu_screen, choose_difficulty_screen, highscores_screen, play_game_screen, game_over_screen, data_save_screen) = screens;

    ui.set_screen(choose_difficulty_screen);
    ui.add_layer(LinearLayout::vertical()
        .child(Button::new("Easy", move |ui| {
            ui.set_screen(data_save_screen);
            difficulty_to_ui(ui, data_save_screen, play_game_screen, poke_a_mango::ops::Difficulty::Easy);
        }))
        .child(Button::new("Normal", move |ui| {
            ui.set_screen(data_save_screen);
            difficulty_to_ui(ui, data_save_screen, play_game_screen, poke_a_mango::ops::Difficulty::Normal);
        }))
        .child(Button::new("Hard", move |ui| {
            ui.set_screen(data_save_screen);
            difficulty_to_ui(ui, data_save_screen, play_game_screen, poke_a_mango::ops::Difficulty::Hard);
        }))
        .child(Button::new("Back", move |ui| ui.set_screen(main_menu_screen))));

    ui.set_screen(data_save_screen);
    ui.add_layer(TextView::new("").with_id("fruit"));
    ui.add_layer(TextView::new("0").with_id("score"));
    ui.add_layer(TextView::new("0").with_id("difficulty"));

    ui.set_screen(play_game_screen);
    ui.add_layer(LinearLayout::vertical()
        .child(TextView::new("Poke a mango!").center())
        .child(TextView::new("Current fruit: Mango").with_id("mango_label"))
        .child(Button::new("Poke it!", {
            let p = p.to_path_buf();
            move |ui| {
                let was_mango = mango_butan_press_common(ui, data_save_screen, play_game_screen);

                if was_mango {
                    update_score_in_ui(ui, data_save_screen, play_game_screen, 1);
                } else {
                    setup_game_over(game_over_screen, data_save_screen, main_menu_screen, ui, &p);
                }
            }
        }))
        .child(Button::new("Ignore it", move |ui| {
            mango_butan_press_common(ui, data_save_screen, play_game_screen);
        })));

    ui.set_screen(main_menu_screen);
    ui.add_layer(LinearLayout::vertical()
        .child(Button::new("Start game", move |ui| ui.set_screen(choose_difficulty_screen)))
        .child(Button::new("Highscores", {
            let p = p.to_path_buf();
            move |ui| setup_leaderboard(highscores_screen, main_menu_screen, ui, &p)
        }))
        .child(Button::new("Exit", Cursive::quit)));
}

fn setup_leaderboard(screen: ScreenId, back_screen: ScreenId, ui: &mut Cursive, p: &Path) {
    ui.set_screen(screen);
    let screen = ui.screen_mut();
    screen.pop_layer();

    let ldrbrd = leaderboard_to_string_form(poke_a_mango::ops::Leader::read(&p.join("leaderboard.toml")).expect("Failed to load leaderboard"));
    let mut layout = LinearLayout::vertical();
    for ldr in ldrbrd.into_iter() {
        layout.add_child(TextView::new(ldr));
    }
    layout.add_child(Button::new("Back", move |ui| ui.set_screen(back_screen)));

    screen.add_layer(layout);
}

fn setup_game_over(screen: ScreenId, data_screen: ScreenId, back_screen: ScreenId, ui: &mut Cursive, p: &Path) {
    let score = update_score_in_ui(ui, data_screen, screen, 0);

    let screen = ui.screen_mut();
    screen.pop_layer();

    let pe = p.to_path_buf();
    let pd = p.to_path_buf();
    screen.add_layer(LinearLayout::vertical()
        .child(TextView::new("Game over!"))
        .child(TextView::new(format!("Score: {}", score)))
        .child(EditView::new()
            .content("Your name")
            .on_submit(move |ui, name| finish_game(ui, data_screen, back_screen, &pe, name.to_string(), score))
            .with_id("name_edit"))
        .child(Button::new("Done", move |ui| {
            let name = {
                let ref mut name: &mut EditView = ui.find(&Selector::Id("name_edit")).unwrap();
                name.get_content().as_ref().clone()
            };
            finish_game(ui, data_screen, back_screen, &pd, name, score);
        }))
        .child(Button::new("Back", move |ui| reset_game_data(ui, data_screen, back_screen))));
}

fn leaderboard_to_string_form(ldrbrd: Vec<poke_a_mango::ops::Leader>) -> Vec<String> {
    let mut tw = TabWriter::new(vec![]);

    let leader_n = cmp::min(ldrbrd.len(), 10);
    for ldr in ldrbrd.into_iter().take(leader_n) {
        writeln!(&mut tw, "{}\t{}", ldr.name, ldr.score).unwrap();
    }

    tw.flush().unwrap();
    String::from_utf8(tw.unwrap()).unwrap().lines().map(str::to_string).collect()
}

fn fruit_from_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId) -> Option<usize> {
    ui.set_screen(data);
    let fruit = {
        let ref mut fruit: &mut TextView = ui.find(&Selector::Id("fruit")).unwrap();
        match fruit.get_content() {
            "" => None,
            l => Some(usize::from_str(l).unwrap()),
        }
    };
    ui.set_screen(back);
    fruit
}

fn fruit_to_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId, new_fruit: Option<usize>) {
    ui.set_screen(data);
    {
        let ref mut fruit: &mut TextView = ui.find(&Selector::Id("fruit")).unwrap();
        fruit.set_content(new_fruit.as_ref().map(usize::to_string).unwrap_or(String::new()));
    }
    ui.set_screen(back);
}

fn difficulty_from_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId) -> poke_a_mango::ops::Difficulty {
    ui.set_screen(data);
    let difficulty = {
        let ref mut difficulty: &mut TextView = ui.find(&Selector::Id("difficulty")).unwrap();
        poke_a_mango::ops::Difficulty::from_numeric(u32::from_str(difficulty.get_content()).unwrap()).unwrap()
    };
    ui.set_screen(back);
    difficulty
}

fn difficulty_to_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId, dfc: poke_a_mango::ops::Difficulty) {
    ui.set_screen(data);
    {
        let ref mut difficulty: &mut TextView = ui.find(&Selector::Id("difficulty")).unwrap();
        difficulty.set_content(dfc.numeric().to_string());
    }
    ui.set_screen(back);
}

fn update_score_in_ui(ui: &mut Cursive, data: ScreenId, back: ScreenId, by: u64) -> u64 {
    ui.set_screen(data);
    let score = {
        let ref mut score: &mut TextView = ui.find(&Selector::Id("score")).unwrap();
        let score_n = u64::from_str(score.get_content()).unwrap() + by;
        score.set_content(score_n.to_string());
        score_n
    };
    ui.set_screen(back);
    score
}

fn mango_butan_press_common(ui: &mut Cursive, data: ScreenId, back: ScreenId) -> bool {
    let fruit = fruit_from_ui(ui, data, back);
    let difficulty = difficulty_from_ui(ui, data, back);

    let mut state = poke_a_mango::ops::GameState::Playing {
        difficulty: difficulty,
        score: 0,
        fruit: fruit,
    };
    poke_a_mango::ops::state::tick_mango(&mut state);
    if let poke_a_mango::ops::GameState::Playing { fruit, .. } = state {
        fruit_to_ui(ui, data, back, fruit);

        let ref mut mango_label: &mut TextView = ui.find(&Selector::Id("mango_label")).expect("mango_label");
        mango_label.set_content(format!("Current fruit: {}", poke_a_mango::util::fruit_name(&fruit)));
    }

    fruit.is_none()
}

fn finish_game(ui: &mut Cursive, data: ScreenId, back: ScreenId, p: &Path, name: String, score: u64) {
    let mut s = poke_a_mango::ops::GameState::GameOver {
        difficulty: difficulty_from_ui(ui, data, data),
        score: score,
        name: name,
    };
    poke_a_mango::ops::state::submit_name(&mut s);
    if let poke_a_mango::ops::GameState::GameEnded { name, score } = s {
        poke_a_mango::ops::Leader::append(poke_a_mango::ops::Leader::now(name, score), &p.join("leaderboard.toml"))
            .expect("Failed to add leader to leaderboard");
    }

    reset_game_data(ui, data, back);
}

fn reset_game_data(ui: &mut Cursive, data: ScreenId, back: ScreenId) {
    ui.set_screen(data);
    {
        let ref mut fruit: &mut TextView = ui.find(&Selector::Id("fruit")).unwrap();
        fruit.set_content("");
    }
    {
        let ref mut score: &mut TextView = ui.find(&Selector::Id("score")).unwrap();
        score.set_content("0");
    }
    ui.set_screen(back);
}
<|endoftext|>


<commit_before>//! Functions for rendering markdown.

use pulldown_cmark::{Parser, html};
use pulldown_cmark::{OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};

/// Renders a markdown string to an HTML string.
///
/// This function enables the following extensions:
///
/// - Autolinking email addresses and URLs
/// - Fenced code blocks
/// - Tables
pub fn to_html(markdown: &str) -> String {
    let parser = Parser::new_ext(markdown, OPTION_ENABLE_TABLES | OPTION_ENABLE_FOOTNOTES);

    let mut output = String::new();
    html::push_html(&mut output, parser);

    output
}
<commit_msg>update `to_html` docs<commit_after>//! Functions for rendering markdown.

use pulldown_cmark::{Parser, html};
use pulldown_cmark::{OPTION_ENABLE_TABLES, OPTION_ENABLE_FOOTNOTES};

/// Renders a markdown string to an HTML string.
///
/// This function renders markdown according to the [CommonMark] spec with the following extensions
/// enabled:
///
/// - Tables
/// - Footnotes
///
/// [CommonMark]: http://commonmark.org/
pub fn to_html(markdown: &str) -> String {
    let parser = Parser::new_ext(markdown, OPTION_ENABLE_TABLES | OPTION_ENABLE_FOOTNOTES);

    let mut output = String::new();
    html::push_html(&mut output, parser);

    output
}
<|endoftext|>


<commit_before>use std::fmt;
use std::marker::PhantomData;

use maud::RenderOnce;
use pulldown_cmark::{ Parser, Event };

use render;

/// The adapter that allows rendering markdown inside a `maud` macro.
///
/// # Examples
///
/// ```
/// # #![feature(plugin)]
/// # #![plugin(maud_macros)]
/// # extern crate maud;
/// # extern crate maud_pulldown_cmark;
/// # use maud_pulldown_cmark::Markdown;
/// # fn main() {
/// let markdown = "
///  1. A list
///  2. With some
///  3. <span>Inline html</span>
/// ";
///
/// let buffer = html! {
///   div {
///     (Markdown::from_string(markdown))
///   }
/// };
///
/// println!("{}", buffer.into_string());
/// # }
/// ```
///
/// ```
/// # #![feature(plugin)]
/// # #![plugin(maud_macros)]
/// # extern crate maud;
/// # extern crate pulldown_cmark;
/// # extern crate maud_pulldown_cmark;
/// # use pulldown_cmark::{ Parser, Event };
/// # use maud_pulldown_cmark::Markdown;
/// # fn main() {
/// let markdown = "
///  1. A list
///  2. With some
///  3. <span>Inline html</span>
/// ";
///
/// let events = Parser::new(markdown).map(|ev| match ev {
///   // Escape inline html
///   Event::Html(html) | Event::InlineHtml(html) => Event::Text(html),
///   _ => ev,
/// });
///
/// let buffer = html! {
///   div {
///     (Markdown::from_events(events))
///   }
/// };
///
/// println!("{}", buffer.into_string());
/// # }
/// ```
pub struct Markdown<'a, I: 'a + Iterator<Item=Event<'a>>> {
  events: I,
  config: render::Config,
  phantom: PhantomData<&'a I>,
}

impl<'a> Markdown<'a, Parser<'a>> {
  /// To allow rendering from a string.
  pub fn from_string(s: &'a str) -> Markdown<'a, Parser<'a>> {
    Markdown {
      events: Parser::new(s),
      config: render::Config {
        header_ids: false,
      },
      phantom: PhantomData,
    }
  }
}

impl<'a, I: 'a + Iterator<Item=Event<'a>>> Markdown<'a, I> {
  /// To allow rendering from a stream of events (useful for modifying the output of the general parser).
  pub fn from_events(events: I) -> Markdown<'a, I> {
    Markdown {
      events: events,
      config: render::Config {
        header_ids: false,
      },
      phantom: PhantomData,
    }
  }

  /// Generate ids for all headers, lowercases the text in the header and replaces spaces with -.
  ///
  /// # Examples
  ///
  /// ```
  /// # #![feature(plugin)]
  /// # #![plugin(maud_macros)]
  /// # extern crate maud;
  /// # extern crate maud_pulldown_cmark;
  /// # use maud_pulldown_cmark::Markdown;
  /// # fn main() {
  // TODO: Work out how to escape the # so this can go on the next line
  /// let markdown = "# Header
  /// ## A Sub Header
  /// ";
  ///
  /// let buffer = html! {
  ///   (Markdown::from_string(markdown).with_header_ids())
  /// }:
  ///
  /// assert_eq!(buffer.into_string(), "<h1 id=\"header\">Header</h1>\n<h2 id=\"a-sub-header\">A Sub Header</h2>\n");
  /// # }
  /// ```
  pub fn with_header_ids(self) -> Markdown<'a, I> {
    Markdown {
      config: render::Config {
        header_ids: true,
        ..self.config
      },
      ..self
    }
  }
}

impl<'a, I: 'a + Iterator<Item=Event<'a>>> RenderOnce for Markdown<'a, I> {
  fn render_once(self, w: &mut fmt::Write) -> fmt::Result {
    render::render_events(self.config, self.events, w)
  }
}

#[cfg(test)]
mod tests {
  use super::Markdown;
  use maud::RenderOnce;
  use pulldown_cmark::{ Parser, Event };

  impl<'a, I: 'a + Iterator<Item=Event<'a>>> Markdown<'a, I> {
    pub fn render(self) -> String {
      let mut buffer = String::new();
      self.render_once(&mut buffer).unwrap();
      buffer
    }
  }

  #[test]
  pub fn test_from_string() {
    let markdown = "
 1. A list
 2. With some
 3. Inline html
    ";

    let buffer = Markdown::from_string(markdown).render();
    assert_eq!(buffer, "
\n
A list
\n
With some
\n
Inline html
\n
\n");
  }

  #[test]
  pub fn test_from_events() {
    let markdown = "
 1. A list
 2. With some
 3. Inline html
    ";

    let events = Parser::new(markdown).map(|ev| match ev {
      // Escape inline html
      Event::Html(html) | Event::InlineHtml(html) => Event::Text(html),
      _ => ev,
    });

    let buffer = Markdown::from_events(events).render();
    assert_eq!(buffer, "
\n
A list
\n
With some
\n
<span>Inline html</span>
\n
\n");
  }

  #[test]
  pub fn test_without_header_ids() {
    let markdown = "
# Header
## A Sub Header
    ";

    let buffer = Markdown::from_string(markdown).render();
    assert_eq!(buffer, "
Header
\n
A Sub Header
\n");
  }

  #[test]
  pub fn test_with_header_ids() {
    let markdown = "
# Header
## A Sub Header
    ";

    let buffer = Markdown::from_string(markdown).with_header_ids().render();
    assert_eq!(buffer, "
Header
\n
A Sub Header
\n");
  }

  #[test]
  pub fn test_with_header_ids_and_linked_inline_image() {
    let markdown = "
# Header [![an image](http://example.com/image)](http://example.com)
    ";

    let buffer = Markdown::from_string(markdown).with_header_ids().render();
    assert_eq!(buffer, "
Header
\n");
  }
}
<commit_msg>Fix example<commit_after>use std::fmt;
use std::marker::PhantomData;

use maud::RenderOnce;
use pulldown_cmark::{ Parser, Event };

use render;

/// The adapter that allows rendering markdown inside a `maud` macro.
///
/// # Examples
///
/// ```
/// # #![feature(plugin)]
/// # #![plugin(maud_macros)]
/// # extern crate maud;
/// # extern crate maud_pulldown_cmark;
/// # use maud_pulldown_cmark::Markdown;
/// # fn main() {
/// let markdown = "
///  1. A list
///  2. With some
///  3. <span>Inline html</span>
/// ";
///
/// let buffer = html! {
///   div {
///     (Markdown::from_string(markdown))
///   }
/// };
///
/// println!("{}", buffer.into_string());
/// # }
/// ```
///
/// ```
/// # #![feature(plugin)]
/// # #![plugin(maud_macros)]
/// # extern crate maud;
/// # extern crate pulldown_cmark;
/// # extern crate maud_pulldown_cmark;
/// # use pulldown_cmark::{ Parser, Event };
/// # use maud_pulldown_cmark::Markdown;
/// # fn main() {
/// let markdown = "
///  1. A list
///  2. With some
///  3. <span>Inline html</span>
/// ";
///
/// let events = Parser::new(markdown).map(|ev| match ev {
///   // Escape inline html
///   Event::Html(html) | Event::InlineHtml(html) => Event::Text(html),
///   _ => ev,
/// });
///
/// let buffer = html! {
///   div {
///     (Markdown::from_events(events))
///   }
/// };
///
/// println!("{}", buffer.into_string());
/// # }
/// ```
pub struct Markdown<'a, I: 'a + Iterator<Item=Event<'a>>> {
  events: I,
  config: render::Config,
  phantom: PhantomData<&'a I>,
}

impl<'a> Markdown<'a, Parser<'a>> {
  /// To allow rendering from a string.
  pub fn from_string(s: &'a str) -> Markdown<'a, Parser<'a>> {
    Markdown {
      events: Parser::new(s),
      config: render::Config {
        header_ids: false,
      },
      phantom: PhantomData,
    }
  }
}

impl<'a, I: 'a + Iterator<Item=Event<'a>>> Markdown<'a, I> {
  /// To allow rendering from a stream of events (useful for modifying the output of the general parser).
  pub fn from_events(events: I) -> Markdown<'a, I> {
    Markdown {
      events: events,
      config: render::Config {
        header_ids: false,
      },
      phantom: PhantomData,
    }
  }

  /// Generate ids for all headers, lowercases the text in the header and replaces spaces with -.
  ///
  /// # Examples
  ///
  /// ```
  /// # #![feature(plugin)]
  /// # #![plugin(maud_macros)]
  /// # extern crate maud;
  /// # extern crate maud_pulldown_cmark;
  /// # use maud_pulldown_cmark::Markdown;
  /// # fn main() {
  // TODO: Work out how to escape the # so this can go on the next line
  /// let markdown = "# Header
  /// ## A Sub Header
  /// ";
  ///
  /// let buffer = html!(
  ///   (Markdown::from_string(markdown).with_header_ids())
  /// );
  ///
  /// assert_eq!(buffer.into_string(), "<h1 id=\"header\">Header</h1>\n<h2 id=\"a-sub-header\">A Sub Header</h2>\n");
  /// # }
  /// ```
  pub fn with_header_ids(self) -> Markdown<'a, I> {
    Markdown {
      config: render::Config {
        header_ids: true,
        ..self.config
      },
      ..self
    }
  }
}

impl<'a, I: 'a + Iterator<Item=Event<'a>>> RenderOnce for Markdown<'a, I> {
  fn render_once(self, w: &mut fmt::Write) -> fmt::Result {
    render::render_events(self.config, self.events, w)
  }
}

#[cfg(test)]
mod tests {
  use super::Markdown;
  use maud::RenderOnce;
  use pulldown_cmark::{ Parser, Event };

  impl<'a, I: 'a + Iterator<Item=Event<'a>>> Markdown<'a, I> {
    pub fn render(self) -> String {
      let mut buffer = String::new();
      self.render_once(&mut buffer).unwrap();
      buffer
    }
  }

  #[test]
  pub fn test_from_string() {
    let markdown = "
 1. A list
 2. With some
 3. Inline html
    ";

    let buffer = Markdown::from_string(markdown).render();
    assert_eq!(buffer, "
\n
A list
\n
With some
\n
Inline html
\n
\n");
  }

  #[test]
  pub fn test_from_events() {
    let markdown = "
 1. A list
 2. With some
 3. Inline html
    ";

    let events = Parser::new(markdown).map(|ev| match ev {
      // Escape inline html
      Event::Html(html) | Event::InlineHtml(html) => Event::Text(html),
      _ => ev,
    });

    let buffer = Markdown::from_events(events).render();
    assert_eq!(buffer, "
\n
A list
\n
With some
\n
<span>Inline html</span>
\n
\n");
  }

  #[test]
  pub fn test_without_header_ids() {
    let markdown = "
# Header
## A Sub Header
    ";

    let buffer = Markdown::from_string(markdown).render();
    assert_eq!(buffer, "
Header
\n
A Sub Header
\n");
  }

  #[test]
  pub fn test_with_header_ids() {
    let markdown = "
# Header
## A Sub Header
    ";

    let buffer = Markdown::from_string(markdown).with_header_ids().render();
    assert_eq!(buffer, "
Header
\n
A Sub Header
\n");
  }

  #[test]
  pub fn test_with_header_ids_and_linked_inline_image() {
    let markdown = "
# Header [![an image](http://example.com/image)](http://example.com)
    ";

    let buffer = Markdown::from_string(markdown).with_header_ids().render();
    assert_eq!(buffer, "
Header
\n");
  }
}
<|endoftext|>


<commit_before>mod figure;

use stamp;
use summary::Summary;

struct Boxplot {
    box_lo: f64,
    box_mid: f64,
    box_hi: f64,
    marker: f64,
    wh_lo: f64,
    wh_hi: f64,
}

impl Boxplot {
    fn from_summary(summary: &Summary) -> Self {
        let range = summary.max() - summary.min();
        let n = |x| (x - summary.min()) / range;

        Boxplot {
            box_lo: n(summary.percentile(0.25).unwrap()),
            box_mid: n(summary.median()),
            box_hi: n(summary.percentile(0.75).unwrap()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min()),
            wh_hi: n(summary.max()),
        }
    }
}

struct BoxplotCols {
    box_lo: usize,
    box_mid: usize,
    box_hi: usize,
    marker: usize,
    wh_lo: usize,
    wh_hi: usize,
}

impl BoxplotCols {
    fn new(data: &Boxplot, width: usize) -> Self {
        let max_col  = (width - 1) as f64;
        let to_col = |x: f64| (x * max_col).floor() as usize;

        BoxplotCols {
            box_lo: to_col(data.box_lo),
            box_mid: to_col(data.box_mid),
            box_hi: to_col(data.box_hi),
            marker: to_col(data.marker),
            wh_lo: to_col(data.wh_lo),
            wh_hi: to_col(data.wh_hi),
        }
    }
}

struct RowChars {
    wh_lo: &'static str,
    wh_lo_box_lo_fill: &'static str,
    box_lo: &'static str,
    box_lo_box_mid_fill: &'static str,
    box_mid: &'static str,
    box_mid_box_hi_fill: &'static str,
    box_hi: &'static str,
    box_hi_wh_hi_fill: &'static str,
    wh_hi: &'static str,
}

impl RowChars {
    pub fn render(&self, row: &mut Vec<String>, cols: &BoxplotCols) {
        row[cols.wh_lo] = self.wh_lo.to_string();
        for i in (cols.wh_lo + 1)..cols.box_lo {
            row[i] = self.wh_lo_box_lo_fill.to_string();
        }
        row[cols.box_lo] = self.box_lo.to_string();
        for i in (cols.box_lo + 1)..cols.box_mid {
            row[i] = self.box_lo_box_mid_fill.to_string();
        }
        row[cols.box_mid] = self.box_mid.to_string();
        for i in (cols.box_mid + 1)..cols.box_hi {
            row[i] = self.box_mid_box_hi_fill.to_string();
        }
        row[cols.box_hi] = self.box_hi.to_string();
        for i in (cols.box_hi + 1)..cols.wh_hi {
            row[i] = self.box_hi_wh_hi_fill.to_string();
        }
        row[cols.wh_hi] = self.wh_hi.to_string();
    }
}

struct BoxplotChars {
    marker: &'static str,
    rows: [RowChars; 3],
}

impl BoxplotChars {
    pub fn render(&self, summary: &Summary, width: usize) -> String {
        let data = Boxplot::from_summary(summary);
        let cols = BoxplotCols::new(&data, width);

        let mut plot = Plot::new(width);

        self.rows[0].render(&mut plot.0, &cols);
        self.rows[1].render(&mut plot.1, &cols);
        self.rows[2].render(&mut plot.2, &cols);

        plot.render()
    }
}

static ASCII_CHARS: BoxplotChars = BoxplotChars {
    marker: "x",
    rows: [
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
        RowChars {
            wh_lo: "|",
            wh_lo_box_lo_fill: "-",
            box_lo: "|",
            box_lo_box_mid_fill: " ",
            box_mid: "|",
            box_mid_box_hi_fill: " ",
            box_hi: "|",
            box_hi_wh_hi_fill: "-",
            wh_hi: "|",
        },
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
    ],
};

static UNICODE_CHARS: BoxplotChars = BoxplotChars {
    marker: "✕",
    rows: [
        RowChars {
            wh_lo: "┬",
            wh_lo_box_lo_fill: " ",
            box_lo: "┌",
            box_lo_box_mid_fill: "─",
            box_mid: "┬",
            box_mid_box_hi_fill: "─",
            box_hi: "┐",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┬",
        },
        RowChars {
            wh_lo: "├",
            wh_lo_box_lo_fill: "─",
            box_lo: "┤",
            box_lo_box_mid_fill: " ",
            box_mid: "│",
            box_mid_box_hi_fill: " ",
            box_hi: "├",
            box_hi_wh_hi_fill: "─",
            wh_hi: "┤",
        },
        RowChars {
            wh_lo: "┴",
            wh_lo_box_lo_fill: " ",
            box_lo: "└",
            box_lo_box_mid_fill: "─",
            box_mid: "┴",
            box_mid_box_hi_fill: "─",
            box_hi: "┘",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┴",
        },
    ],
};

fn make_row(width: usize) -> Vec<String> {
    use std::iter::repeat;

    let mut row = vec![];
    row.extend(repeat(String::from("")).take(width));

    row
}

struct Plot(Vec<String>, Vec<String>, Vec<String>);

impl Plot {
    fn new(width: usize) -> Self {
        Plot(make_row(width), make_row(width), make_row(width))
    }

    fn render(&self) -> String {
        let rows = vec![
            self.0.join(""),
            self.1.join(""),
            self.2.join(""),
        ];

        rows.join("\n")
    }
}

pub fn summary_plot(summary: &Summary, width: usize, ascii: bool) -> String {
    let plot_style = if ascii { &ASCII_CHARS } else { &UNICODE_CHARS };

    plot_style.render(summary, width)
}

pub fn comparison_plot(
    summary1: &Summary,
    summary2: &Summary,
    width: usize,
    ascii: bool,
    border: bool,
) -> String {
    let padding = if border { 2 } else { 0 };
    let border_style = if ascii {
        figure::ASCII_BORDER
    } else {
        figure::UNICODE_BORDER
    };

    let min = summary1.min().min(summary2.min());
    let max = summary1.max().max(summary2.max());

    let range = max - min;
    let range1 = summary1.max() - summary1.min();
    let range2 = summary2.max() - summary2.min();

    let proportion1 = range1 / range;
    let proportion2 = range2 / range;

    let width1 = (proportion1 * (width as f64)).floor() as usize - (padding * 2);
    let width2 = (proportion2 * (width as f64)).floor() as usize - (padding * 2);

    let plot1 = stamp::Stamp::new(&summary_plot(&summary1, width1, ascii)).unwrap();
    let plot2 = stamp::Stamp::new(&summary_plot(&summary2, width2, ascii)).unwrap();

    let offset1 = {
        let neg_proportion = (summary1.min() - min) / range;
        (neg_proportion * (width as f64)).floor() as usize
    } + padding;
    let offset2 = {
        let neg_proportion = (summary2.min() - min) / range;
        (neg_proportion * (width as f64)).floor() as usize
    } + padding;

    let height = plot1.height() + plot2.height() + (padding * 2);

    let base = if border {
        figure::Border::new(border_style, width, height).render()
    } else {
        figure::Filled::blank(width, height).render()
    };

    let mut plots = stamp::Stamp::new(&base).unwrap();
    plots = plots.layer(&plot1, offset1, padding).unwrap();
    plots = plots.layer(&plot2, offset2, plot1.height() + padding).unwrap();

    plots.render()
}
<commit_msg>Render means in summary plots<commit_after>mod figure;

use stamp;
use summary::Summary;

struct Boxplot {
    box_lo: f64,
    box_mid: f64,
    box_hi: f64,
    marker: f64,
    wh_lo: f64,
    wh_hi: f64,
}

impl Boxplot {
    fn from_summary(summary: &Summary) -> Self {
        let range = summary.max() - summary.min();
        let n = |x| (x - summary.min()) / range;

        Boxplot {
            box_lo: n(summary.percentile(0.25).unwrap()),
            box_mid: n(summary.median()),
            box_hi: n(summary.percentile(0.75).unwrap()),
            marker: n(summary.mean()),
            wh_lo: n(summary.min()),
            wh_hi: n(summary.max()),
        }
    }
}

struct BoxplotCols {
    box_lo: usize,
    box_mid: usize,
    box_hi: usize,
    marker: usize,
    wh_lo: usize,
    wh_hi: usize,
}

impl BoxplotCols {
    fn new(data: &Boxplot, width: usize) -> Self {
        let max_col  = (width - 1) as f64;
        let to_col = |x: f64| (x * max_col).floor() as usize;

        BoxplotCols {
            box_lo: to_col(data.box_lo),
            box_mid: to_col(data.box_mid),
            box_hi: to_col(data.box_hi),
            marker: to_col(data.marker),
            wh_lo: to_col(data.wh_lo),
            wh_hi: to_col(data.wh_hi),
        }
    }
}

struct RowChars {
    wh_lo: &'static str,
    wh_lo_box_lo_fill: &'static str,
    box_lo: &'static str,
    box_lo_box_mid_fill: &'static str,
    box_mid: &'static str,
    box_mid_box_hi_fill: &'static str,
    box_hi: &'static str,
    box_hi_wh_hi_fill: &'static str,
    wh_hi: &'static str,
}

impl RowChars {
    pub fn render(&self, row: &mut Vec<String>, cols: &BoxplotCols) {
        row[cols.wh_lo] = self.wh_lo.to_string();
        for i in (cols.wh_lo + 1)..cols.box_lo {
            row[i] = self.wh_lo_box_lo_fill.to_string();
        }
        row[cols.box_lo] = self.box_lo.to_string();
        for i in (cols.box_lo + 1)..cols.box_mid {
            row[i] = self.box_lo_box_mid_fill.to_string();
        }
        row[cols.box_mid] = self.box_mid.to_string();
        for i in (cols.box_mid + 1)..cols.box_hi {
            row[i] = self.box_mid_box_hi_fill.to_string();
        }
        row[cols.box_hi] = self.box_hi.to_string();
        for i in (cols.box_hi + 1)..cols.wh_hi {
            row[i] = self.box_hi_wh_hi_fill.to_string();
        }
        row[cols.wh_hi] = self.wh_hi.to_string();
    }
}

struct BoxplotChars {
    marker: &'static str,
    rows: [RowChars; 3],
}

impl BoxplotChars {
    pub fn render(&self, summary: &Summary, width: usize) -> String {
        let data = Boxplot::from_summary(summary);
        let cols = BoxplotCols::new(&data, width);

        let mut plot = Plot::new(width);

        self.rows[0].render(&mut plot.0, &cols);
        self.rows[1].render(&mut plot.1, &cols);
        self.rows[2].render(&mut plot.2, &cols);

        let no_marker = plot.render();

        let base = stamp::Stamp::new(&no_marker).unwrap();
        let marker = stamp::Stamp::new(self.marker).unwrap();
        let layered = base.layer(&marker, cols.marker, 1).unwrap();

        layered.render()
    }
}

static ASCII_CHARS: BoxplotChars = BoxplotChars {
    marker: "x",
    rows: [
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
        RowChars {
            wh_lo: "|",
            wh_lo_box_lo_fill: "-",
            box_lo: "|",
            box_lo_box_mid_fill: " ",
            box_mid: "|",
            box_mid_box_hi_fill: " ",
            box_hi: "|",
            box_hi_wh_hi_fill: "-",
            wh_hi: "|",
        },
        RowChars {
            wh_lo: " ",
            wh_lo_box_lo_fill: " ",
            box_lo: "+",
            box_lo_box_mid_fill: "-",
            box_mid: "+",
            box_mid_box_hi_fill: "-",
            box_hi: "+",
            box_hi_wh_hi_fill: " ",
            wh_hi: " ",
        },
    ],
};

static UNICODE_CHARS: BoxplotChars = BoxplotChars {
    marker: "✕",
    rows: [
        RowChars {
            wh_lo: "┬",
            wh_lo_box_lo_fill: " ",
            box_lo: "┌",
            box_lo_box_mid_fill: "─",
            box_mid: "┬",
            box_mid_box_hi_fill: "─",
            box_hi: "┐",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┬",
        },
        RowChars {
            wh_lo: "├",
            wh_lo_box_lo_fill: "─",
            box_lo: "┤",
            box_lo_box_mid_fill: " ",
            box_mid: "│",
            box_mid_box_hi_fill: " ",
            box_hi: "├",
            box_hi_wh_hi_fill: "─",
            wh_hi: "┤",
        },
        RowChars {
            wh_lo: "┴",
            wh_lo_box_lo_fill: " ",
            box_lo: "└",
            box_lo_box_mid_fill: "─",
            box_mid: "┴",
            box_mid_box_hi_fill: "─",
            box_hi: "┘",
            box_hi_wh_hi_fill: " ",
            wh_hi: "┴",
        },
    ],
};

fn make_row(width: usize) -> Vec<String> {
    use std::iter::repeat;

    let mut row = vec![];
    row.extend(repeat(String::from("")).take(width));

    row
}

struct Plot(Vec<String>, Vec<String>, Vec<String>);

impl Plot {
    fn new(width: usize) -> Self {
        Plot(make_row(width), make_row(width), make_row(width))
    }

    fn render(&self) -> String {
        let rows = vec![
            self.0.join(""),
            self.1.join(""),
            self.2.join(""),
        ];

        rows.join("\n")
    }
}

pub fn summary_plot(summary: &Summary, width: usize, ascii: bool) -> String {
    let plot_style = if ascii { &ASCII_CHARS } else { &UNICODE_CHARS };

    plot_style.render(summary, width)
}

pub fn comparison_plot(
    summary1: &Summary,
    summary2: &Summary,
    width: usize,
    ascii: bool,
    border: bool,
) -> String {
    let padding = if border { 2 } else { 0 };
    let border_style = if ascii {
        figure::ASCII_BORDER
    } else {
        figure::UNICODE_BORDER
    };

    let min = summary1.min().min(summary2.min());
    let max = summary1.max().max(summary2.max());

    let range = max - min;
    let range1 = summary1.max() - summary1.min();
    let range2 = summary2.max() - summary2.min();

    let proportion1 = range1 / range;
    let proportion2 = range2 / range;

    let width1 = (proportion1 * (width as f64)).floor() as usize - (padding * 2);
    let width2 = (proportion2 * (width as f64)).floor() as usize - (padding * 2);

    let plot1 = stamp::Stamp::new(&summary_plot(&summary1, width1, ascii)).unwrap();
    let plot2 = stamp::Stamp::new(&summary_plot(&summary2, width2, ascii)).unwrap();

    let offset1 = {
        let neg_proportion = (summary1.min() - min) / range;
        (neg_proportion * (width as f64)).floor() as usize
    } + padding;
    let offset2 = {
        let neg_proportion = (summary2.min() - min) / range;
        (neg_proportion * (width as f64)).floor() as usize
    } + padding;

    let height = plot1.height() + plot2.height() + (padding * 2);

    let base = if border {
        figure::Border::new(border_style, width, height).render()
    } else {
        figure::Filled::blank(width, height).render()
    };

    let mut plots = stamp::Stamp::new(&base).unwrap();
    plots = plots.layer(&plot1, offset1, padding).unwrap();
    plots = plots.layer(&plot2, offset2, plot1.height() + padding).unwrap();

    plots.render()
}
<|endoftext|>


<commit_before>//! Kafka Producer
//!
//! A multi-topic capable producer for a Kafka cluster providing a
//! convenient API for sending messages.  So far the producer has only
//! synchronous capabilities.
//!
//! In Kafka, each message is basically a key/value pair.  A `Record`
//! is all the data necessary to produce such a message to Kafka.
//!
//! # Example
//! ```no_run
//! use std::fmt::Write;
//! use kafka::producer::{Producer, Record};
//!
//! let mut producer =
//!     Producer::from_hosts(vec!("localhost:9092".to_owned()))
//!         .with_ack_timeout(1000)
//!         .with_required_acks(1)
//!         .create()
//!         .unwrap();
//!
//! let mut buf = String::with_capacity(2);
//! for i in 0..10 {
//!   let _ = write!(&mut buf, "{}", i); // some computation of the message data to be sent
//!   producer.send(&Record::from_value("my-topic", buf.as_bytes())).unwrap();
//!   buf.clear();
//! }
//! ```
//!
//! In this example, when the method call to `producer.send` returns
//! successfully, we are guaranteed the message is delivered to Kafka
//! and persisted by at least one Kafka broker.  However, when sending
//! multiple messages just like this example, it is more efficient to
//! send them in batches using `Producer::send_all`.

// XXX 0) Always invoke partitioner
// XXX 1) rethink return values for the send_all method
// XXX 2) maintain a background thread to provide an async version of the send* methods
// XXX 3) Handle recoverable errors behind the scenes through retry attempts

use std::collections::HashMap;
use std::fmt;

use client::{self, KafkaClient};
// public re-exports
pub use client::{Compression};
use error::Result;
use utils::TopicPartitionOffsetError;

use ref_slice::ref_slice;

/// The default value for `Builder::with_ack_timeout`.
pub const DEFAULT_ACK_TIMEOUT: i32 = 30 * 1000;

/// The default value for `Builder::with_required_acks`.
pub const DEFAULT_REQUIRED_ACKS: i16 = 1;

// --------------------------------------------------------------------

/// A trait used by `Producer` to obtain the bytes `Record::key` and
/// `Record::value` represent.  This leaves the choice of the types
/// for `key` and `value` with the client.
pub trait AsBytes {
    fn as_bytes(&self) -> &[u8];
}

impl AsBytes for () {
    fn as_bytes(&self) -> &[u8] { &[] }
}

// There seems to be some compiler issue with this:
// impl<T: AsRef<[u8]>> AsBytes for T {
//     fn as_bytes(&self) -> &[u8] { self.as_ref() }
// }

// for now we provide the impl for some standard library types
impl AsBytes for String {
    fn as_bytes(&self) -> &[u8] { self.as_ref() }
}
impl AsBytes for Vec<u8> {
    fn as_bytes(&self) -> &[u8] { self.as_ref() }
}

impl<'a> AsBytes for &'a [u8] {
    fn as_bytes(&self) -> &[u8] { self }
}
impl<'a> AsBytes for &'a str {
    fn as_bytes(&self) -> &[u8] { str::as_bytes(self) }
}

// --------------------------------------------------------------------

/// A structure representing a message to be sent to Kafka through the
/// `Producer` API.  Such a message is basically a key/value pair
/// specifying the target topic and optionally the topic's partition.
pub struct Record<'a, K, V> {
    /// Key data of this (message) record.
    pub key: K,

    /// Value data of this (message) record.
    pub value: V,

    /// Name of the topic this message is supposed to be delivered to.
    pub topic: &'a str,

    /// The partition id of the topic to deliver this message to.
    /// This partition may be `< 0` in which case it is considered
    /// "unspecified".  A `Producer` will then typically try to derive
    /// a partition on its own.
    pub partition: i32,
}

impl<'a, K, V> Record<'a, K, V> {

    /// Convenience function to create a new key/value record with an
    /// "unspecified" partition - this is, a partition set to a negative
    /// value.
    #[inline]
    pub fn from_key_value(topic: &'a str, key: K, value: V) -> Record<'a, K, V> {
        Record { key: key, value: value, topic: topic, partition: -1 }
    }

    /// Convenience method to set the partition.
    #[inline]
    pub fn with_partition(mut self, partition: i32) -> Self {
        self.partition = partition;
        self
    }
}

impl<'a, V> Record<'a, (), V> {
    /// Convenience function to create a new value only record with an
    /// "unspecified" partition - this is, a partition set to a negative
    /// value.
    #[inline]
    pub fn from_value(topic: &'a str, value: V) -> Record<'a, (), V> {
        Record { key: (), value: value, topic: topic, partition: -1 }
    }
}

impl<'a, K: fmt::Debug, V: fmt::Debug> fmt::Debug for Record<'a, K, V> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Record {{ topic: {}, partition: {}, key: {:?}, value: {:?} }}",
               self.topic, self.partition, self.key, self.value)
    }
}

// --------------------------------------------------------------------

/// The Kafka Producer
///
/// See module level documentation.
pub struct Producer<P = DefaultPartitioner> {
    client: KafkaClient,
    state: State<P>,
    config: Config,
}

struct State<P> {
    /// A list of available partition IDs for each topic.
    partition_ids: HashMap<String, Vec<i32>>,
    /// The partitioner to decide how to distribute messages
    partitioner: P,
}

struct Config {
    /// The maximum time in millis to wait for acknowledgements. See
    /// `KafkaClient::produce_messages`.
    ack_timeout: i32,
    /// The number of acks to request. See
    /// `KafkaClient::produce_messages`.
    required_acks: i16,
}

impl Producer {
    /// Starts building a new producer using the given Kafka client.
    pub fn from_client(client: KafkaClient) -> Builder<DefaultPartitioner> {
        Builder::new(Some(client), Vec::new())
    }

    /// Starts building a producer bootstraping internally a new kafka
    /// client from the given kafka hosts.
    pub fn from_hosts(hosts: Vec<String>) -> Builder<DefaultPartitioner> {
        Builder::new(None, hosts)
    }

    /// Destroys this producer returning the underlying kafka client.
    pub fn client(self) -> KafkaClient {
        self.client
    }
}

impl<P: Partitioner> Producer<P> {

    /// Synchronously send the specified message to Kafka.
    pub fn send<'a, K, V>(&mut self, rec: &Record<'a, K, V>)
                          -> Result<()>
        where K: AsBytes, V: AsBytes
    {
        let mut rs = try!(self.send_all(ref_slice(rec)));
        assert_eq!(1, rs.len());
        if let Some(e) = rs.pop().unwrap().error {
            Err(e)
        } else {
            Ok(())
        }
    }

    /// Synchronously send all of the specified messages to Kafka.
    pub fn send_all<'a, K, V>(&mut self, recs: &[Record<'a, K, V>])
                              -> Result<Vec<TopicPartitionOffsetError>>
        where K: AsBytes, V: AsBytes
    {
        let partitioner = &mut self.state.partitioner;
        let partition_ids = &self.state.partition_ids;
        let client = &mut self.client;
        let config = &self.config;

        client.produce_messages(
            config.required_acks, config.ack_timeout,
            recs.into_iter().map(|r| {
                let mut m = client::ProduceMessage {
                    key: to_option(r.key.as_bytes()),
                    value: to_option(r.value.as_bytes()),
                    topic: r.topic,
                    partition: r.partition,
                };
                // XXX always invoke the partitioner

                // XXX needs to be given a `Cluster/Topics` structure, such that a
                // partitioner has the chance to avoid topic look ups.

                if r.partition < 0 {
                    if let Some(ps) = partition_ids.get(r.topic).map(|ps| &ps[..]) {
                        partitioner.partition(&mut m, ps);
                    }
                }
                m
            }))
    }
}

fn to_option(data: &[u8]) -> Option<&[u8]> {
    if data.is_empty() {
        None
    } else {
        Some(data)
    }
}

// --------------------------------------------------------------------

impl<P> State<P> {
    fn new(client: &mut KafkaClient, partitioner: P) -> Result<State<P>> {
        let ts = client.topics();
        let mut ids = HashMap::with_capacity(ts.len());
        for t in ts {
            ids.insert(t.name().to_owned(), t.partition_ids());
        }
        Ok(State{partition_ids: ids, partitioner: partitioner})
    }
}

// --------------------------------------------------------------------

/// A Kafka Producer builder easing the process of setting up various
/// configuration settings.
pub struct Builder<P = DefaultPartitioner> {
    client: Option<KafkaClient>,
    hosts: Vec<String>,
    compression: Compression,
    ack_timeout: i32,
    required_acks: i16,
    partitioner: P,
}

impl Builder {
    fn new(client: Option<KafkaClient>, hosts: Vec<String>)
           -> Builder<DefaultPartitioner>
    {
        let mut b = Builder {
            client: client,
            hosts: hosts,
            compression: client::DEFAULT_COMPRESSION,
            ack_timeout: DEFAULT_ACK_TIMEOUT,
            required_acks: DEFAULT_REQUIRED_ACKS,
            partitioner: DefaultPartitioner::default(),
        };
        if let Some(ref c) = b.client {
            b.compression = c.compression();
        }
        b
    }

    /// Sets the compression algorithm to use when sending out data.
    ///
    /// See `KafkaClient::set_compression`.
    pub fn with_compression(mut self, compression: Compression) -> Self {
        self.compression = compression;
        self
    }

    /// Sets the maximum time in milliseconds the kafka brokers can
    /// await the receipt of required acknowledgements (which is
    /// specified through `Builder::with_required_acks`.)  Note that
    /// Kafka explicitely documents this not to be a hard limit.
    pub fn with_ack_timeout(mut self, timeout: i32) -> Self {
        self.ack_timeout = timeout;
        self
    }

    /// Sets how many acknowledgements the kafka brokers should
    /// receive before responding to sent messages.  If it is 0 the
    /// servers will not send any response.  If it is 1, the server
    /// will wait the data is written to the local server log before
    /// sending a replying.  If it is -1 the servers will block until
    /// the messages are committed by all in sync replicas before
    /// replaying.  For any number `> 1` the servers will block
    /// waiting for this number of acknowledgements to occur (but the
    /// servers will never wait for more acknowledgements than there
    /// are in-sync replicas).
    pub fn with_required_acks(mut self, required_acks: i16) -> Self {
        self.required_acks = required_acks;
        self
    }
}

impl<P> Builder<P> {

    /// Sets the partitioner to dispatch when sending messages without
    /// an explicit partition assignment.
    pub fn with_partitioner<Q: Partitioner>(self, partitioner: Q) -> Builder<Q> {
        Builder {
            client: self.client,
            hosts: self.hosts,
            compression: self.compression,
            ack_timeout: self.ack_timeout,
            required_acks: self.required_acks,
            partitioner: partitioner,
        }
    }

    /// Finally creates/builds a new producer based on the so far
    /// supplied settings.
    pub fn create(self) -> Result<Producer<P>> {
        let mut client = match self.client {
            Some(client) => client,
            None => {
                let mut client = KafkaClient::new(self.hosts);
                try!(client.load_metadata_all());
                client
            }
        };
        client.set_compression(self.compression);
        let state = try!(State::new(&mut client, self.partitioner));
        let config = Config {
            ack_timeout: self.ack_timeout,
            required_acks: self.required_acks,
        };
        Ok(Producer{
            client: client,
            state: state,
            config: config,
        })
    }
}

// --------------------------------------------------------------------

/// A partitioner is given a chance to choose/redefine a partition for
/// a message to be sent to Kafka.  See also
/// `Record#with_partition`.
///
/// Implementations can be stateful.
pub trait Partitioner {
    /// Supposed to inspect the given message and if desired re-assign
    /// the message's target partition.  Since the partitioner is
    /// given a mutable reference and may potentially change even more
    /// than just the partition.
    fn partition(&mut self, msg: &mut client::ProduceMessage, partitions: &[i32]);
}

/// As its name implies `DefaultPartitioner` is the default
/// partitioner for `Producer`.
///
/// Every received message with a non-negative value will not be
/// changed by this partitioner and will be merely passed through.
///
/// However, for every message with a negative `partition` it will try
/// to find a target partition. In a very simple manner, it tries to
/// distribute such messsages across available partitions in a round
/// robin fashion.
///
/// This behavior may not suffice every workload.  If you're
/// application is dependent on a particular distribution scheme, you
/// want to provide your own partioner to the `Producer` at
/// initialization time.
///
/// See `Builder::with_partitioner`.
pub struct DefaultPartitioner {
    // ~ a counter incremented with each partitioned message to
    // achieve a different partition assignment for each message
    cntr: u32
}

impl Partitioner for DefaultPartitioner {
    #[allow(unused_variables)]
    fn partition(&mut self, rec: &mut client::ProduceMessage, partitions: &[i32]) {
        if rec.partition < 0 && !partitions.is_empty() {
            // ~ get a partition
            rec.partition = partitions[self.cntr as usize % partitions.len()];
            // ~ update internal state so that the next time we choose
            // a different partition
            self.cntr = self.cntr.wrapping_add(1);
        }
    }
}

impl Default for DefaultPartitioner {
    fn default() -> Self {
        DefaultPartitioner { cntr: 0 }
    }
}
<commit_msg>#! Always invoke partitioner<commit_after>//! Kafka Producer
//!
//! A multi-topic capable producer for a Kafka cluster providing a
//! convenient API for sending messages.  So far the producer has only
//! synchronous capabilities.
//!
//! In Kafka, each message is basically a key/value pair.  A `Record`
//! is all the data necessary to produce such a message to Kafka.
//!
//! # Example
//! ```no_run
//! use std::fmt::Write;
//! use kafka::producer::{Producer, Record};
//!
//! let mut producer =
//!     Producer::from_hosts(vec!("localhost:9092".to_owned()))
//!         .with_ack_timeout(1000)
//!         .with_required_acks(1)
//!         .create()
//!         .unwrap();
//!
//! let mut buf = String::with_capacity(2);
//! for i in 0..10 {
//!   let _ = write!(&mut buf, "{}", i); // some computation of the message data to be sent
//!   producer.send(&Record::from_value("my-topic", buf.as_bytes())).unwrap();
//!   buf.clear();
//! }
//! ```
//!
//! In this example, when the method call to `producer.send` returns
//! successfully, we are guaranteed the message is delivered to Kafka
//! and persisted by at least one Kafka broker.  However, when sending
//! multiple messages just like this example, it is more efficient to
//! send them in batches using `Producer::send_all`.

// XXX 1) rethink return values for the send_all method
// XXX 2) maintain a background thread to provide an async version of the send* methods
// XXX 3) Handle recoverable errors behind the scenes through retry attempts

use std::collections::HashMap;
use std::fmt;

use client::{self, KafkaClient};
// public re-exports
pub use client::{Compression};
use error::Result;
use utils::TopicPartitionOffsetError;

use ref_slice::ref_slice;

/// The default value for `Builder::with_ack_timeout`.
pub const DEFAULT_ACK_TIMEOUT: i32 = 30 * 1000;

/// The default value for `Builder::with_required_acks`.
pub const DEFAULT_REQUIRED_ACKS: i16 = 1;

// --------------------------------------------------------------------

/// A trait used by `Producer` to obtain the bytes `Record::key` and
/// `Record::value` represent.  This leaves the choice of the types
/// for `key` and `value` with the client.
pub trait AsBytes {
    fn as_bytes(&self) -> &[u8];
}

impl AsBytes for () {
    fn as_bytes(&self) -> &[u8] { &[] }
}

// There seems to be some compiler issue with this:
// impl<T: AsRef<[u8]>> AsBytes for T {
//     fn as_bytes(&self) -> &[u8] { self.as_ref() }
// }

// for now we provide the impl for some standard library types
impl AsBytes for String {
    fn as_bytes(&self) -> &[u8] { self.as_ref() }
}
impl AsBytes for Vec<u8> {
    fn as_bytes(&self) -> &[u8] { self.as_ref() }
}

impl<'a> AsBytes for &'a [u8] {
    fn as_bytes(&self) -> &[u8] { self }
}
impl<'a> AsBytes for &'a str {
    fn as_bytes(&self) -> &[u8] { str::as_bytes(self) }
}

// --------------------------------------------------------------------

/// A structure representing a message to be sent to Kafka through the
/// `Producer` API.  Such a message is basically a key/value pair
/// specifying the target topic and optionally the topic's partition.
pub struct Record<'a, K, V> {
    /// Key data of this (message) record.
    pub key: K,

    /// Value data of this (message) record.
    pub value: V,

    /// Name of the topic this message is supposed to be delivered to.
    pub topic: &'a str,

    /// The partition id of the topic to deliver this message to.
    /// This partition may be `< 0` in which case it is considered
    /// "unspecified".  A `Producer` will then typically try to derive
    /// a partition on its own.
    pub partition: i32,
}

impl<'a, K, V> Record<'a, K, V> {

    /// Convenience function to create a new key/value record with an
    /// "unspecified" partition - this is, a partition set to a negative
    /// value.
    #[inline]
    pub fn from_key_value(topic: &'a str, key: K, value: V) -> Record<'a, K, V> {
        Record { key: key, value: value, topic: topic, partition: -1 }
    }

    /// Convenience method to set the partition.
    #[inline]
    pub fn with_partition(mut self, partition: i32) -> Self {
        self.partition = partition;
        self
    }
}

impl<'a, V> Record<'a, (), V> {
    /// Convenience function to create a new value only record with an
    /// "unspecified" partition - this is, a partition set to a negative
    /// value.
    #[inline]
    pub fn from_value(topic: &'a str, value: V) -> Record<'a, (), V> {
        Record { key: (), value: value, topic: topic, partition: -1 }
    }
}

impl<'a, K: fmt::Debug, V: fmt::Debug> fmt::Debug for Record<'a, K, V> {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "Record {{ topic: {}, partition: {}, key: {:?}, value: {:?} }}",
               self.topic, self.partition, self.key, self.value)
    }
}

// --------------------------------------------------------------------

/// The Kafka Producer
///
/// See module level documentation.
pub struct Producer<P = DefaultPartitioner> {
    client: KafkaClient,
    state: State<P>,
    config: Config,
}

struct State<P> {
    /// A list of available partition IDs for each topic.
    partition_ids: HashMap<String, Vec<i32>>,
    /// The partitioner to decide how to distribute messages
    partitioner: P,
}

struct Config {
    /// The maximum time in millis to wait for acknowledgements. See
    /// `KafkaClient::produce_messages`.
    ack_timeout: i32,
    /// The number of acks to request. See
    /// `KafkaClient::produce_messages`.
    required_acks: i16,
}

impl Producer {
    /// Starts building a new producer using the given Kafka client.
    pub fn from_client(client: KafkaClient) -> Builder<DefaultPartitioner> {
        Builder::new(Some(client), Vec::new())
    }

    /// Starts building a producer bootstraping internally a new kafka
    /// client from the given kafka hosts.
    pub fn from_hosts(hosts: Vec<String>) -> Builder<DefaultPartitioner> {
        Builder::new(None, hosts)
    }

    /// Destroys this producer returning the underlying kafka client.
    pub fn client(self) -> KafkaClient {
        self.client
    }
}

impl<P: Partitioner> Producer<P> {

    /// Synchronously send the specified message to Kafka.
    pub fn send<'a, K, V>(&mut self, rec: &Record<'a, K, V>)
                          -> Result<()>
        where K: AsBytes, V: AsBytes
    {
        let mut rs = try!(self.send_all(ref_slice(rec)));
        assert_eq!(1, rs.len());
        if let Some(e) = rs.pop().unwrap().error {
            Err(e)
        } else {
            Ok(())
        }
    }

    /// Synchronously send all of the specified messages to Kafka.
    pub fn send_all<'a, K, V>(&mut self, recs: &[Record<'a, K, V>])
                              -> Result<Vec<TopicPartitionOffsetError>>
        where K: AsBytes, V: AsBytes
    {
        let partitioner = &mut self.state.partitioner;
        let partition_ids = &self.state.partition_ids;
        let client = &mut self.client;
        let config = &self.config;

        client.produce_messages(
            config.required_acks, config.ack_timeout,
            recs.into_iter().map(|r| {
                let mut m = client::ProduceMessage {
                    key: to_option(r.key.as_bytes()),
                    value: to_option(r.value.as_bytes()),
                    topic: r.topic,
                    partition: r.partition,
                };
                partitioner.partition(Topics::new(partition_ids), &mut m);
                m
            }))
    }
}

fn to_option(data: &[u8]) -> Option<&[u8]> {
    if data.is_empty() {
        None
    } else {
        Some(data)
    }
}

// --------------------------------------------------------------------

impl<P> State<P> {
    fn new(client: &mut KafkaClient, partitioner: P) -> Result<State<P>> {
        let ts = client.topics();
        let mut ids = HashMap::with_capacity(ts.len());
        for t in ts {
            ids.insert(t.name().to_owned(), t.partition_ids());
        }
        Ok(State{partition_ids: ids, partitioner: partitioner})
    }
}

// --------------------------------------------------------------------

/// A Kafka Producer builder easing the process of setting up various
/// configuration settings.
pub struct Builder<P = DefaultPartitioner> {
    client: Option<KafkaClient>,
    hosts: Vec<String>,
    compression: Compression,
    ack_timeout: i32,
    required_acks: i16,
    partitioner: P,
}

impl Builder {
    fn new(client: Option<KafkaClient>, hosts: Vec<String>)
           -> Builder<DefaultPartitioner>
    {
        let mut b = Builder {
            client: client,
            hosts: hosts,
            compression: client::DEFAULT_COMPRESSION,
            ack_timeout: DEFAULT_ACK_TIMEOUT,
            required_acks: DEFAULT_REQUIRED_ACKS,
            partitioner: DefaultPartitioner::default(),
        };
        if let Some(ref c) = b.client {
            b.compression = c.compression();
        }
        b
    }

    /// Sets the compression algorithm to use when sending out data.
    ///
    /// See `KafkaClient::set_compression`.
    pub fn with_compression(mut self, compression: Compression) -> Self {
        self.compression = compression;
        self
    }

    /// Sets the maximum time in milliseconds the kafka brokers can
    /// await the receipt of required acknowledgements (which is
    /// specified through `Builder::with_required_acks`.)  Note that
    /// Kafka explicitely documents this not to be a hard limit.
    pub fn with_ack_timeout(mut self, timeout: i32) -> Self {
        self.ack_timeout = timeout;
        self
    }

    /// Sets how many acknowledgements the kafka brokers should
    /// receive before responding to sent messages.  If it is 0 the
    /// servers will not send any response.  If it is 1, the server
    /// will wait the data is written to the local server log before
    /// sending a replying.  If it is -1 the servers will block until
    /// the messages are committed by all in sync replicas before
    /// replaying.  For any number `> 1` the servers will block
    /// waiting for this number of acknowledgements to occur (but the
    /// servers will never wait for more acknowledgements than there
    /// are in-sync replicas).
    pub fn with_required_acks(mut self, required_acks: i16) -> Self {
        self.required_acks = required_acks;
        self
    }
}

impl<P> Builder<P> {

    /// Sets the partitioner to dispatch when sending messages without
    /// an explicit partition assignment.
    pub fn with_partitioner<Q: Partitioner>(self, partitioner: Q) -> Builder<Q> {
        Builder {
            client: self.client,
            hosts: self.hosts,
            compression: self.compression,
            ack_timeout: self.ack_timeout,
            required_acks: self.required_acks,
            partitioner: partitioner,
        }
    }

    /// Finally creates/builds a new producer based on the so far
    /// supplied settings.
    pub fn create(self) -> Result<Producer<P>> {
        let mut client = match self.client {
            Some(client) => client,
            None => {
                let mut client = KafkaClient::new(self.hosts);
                try!(client.load_metadata_all());
                client
            }
        };
        client.set_compression(self.compression);
        let state = try!(State::new(&mut client, self.partitioner));
        let config = Config {
            ack_timeout: self.ack_timeout,
            required_acks: self.required_acks,
        };
        Ok(Producer{
            client: client,
            state: state,
            config: config,
        })
    }
}

// --------------------------------------------------------------------

/// A description of available topics and their available partitions.
///
/// Indented for use by `Partitioner`s.
pub struct Topics<'a> {
    partition_ids: &'a HashMap<String, Vec<i32>>,
}

impl<'a> Topics<'a> {
    fn new(partition_ids: &'a HashMap<String, Vec<i32>>) -> Topics<'a> {
        Topics { partition_ids: partition_ids }
    }

    /// Retrieves a list of the identifiers of available partitions
    /// for the given topic.
    pub fn partition_ids(&self, topic: &'a str) -> Option<&[i32]> {
        self.partition_ids.get(topic).map(|ps| &ps[..])
    }
}

/// A partitioner is given a chance to choose/redefine a partition for
/// a message to be sent to Kafka.  See also
/// `Record#with_partition`.
///
/// Implementations can be stateful.
pub trait Partitioner {
    /// Supposed to inspect the given message and if desired re-assign
    /// the message's target partition.
    ///
    /// `topics` a description of the currently known topics and their
    /// currently available partitions.
    ///
    /// `msg` the message whose partition assignment potentially to
    /// change.
    fn partition(&mut self, topics: Topics, msg: &mut client::ProduceMessage);
}

/// As its name implies `DefaultPartitioner` is the default
/// partitioner for `Producer`.
///
/// Every received message with a non-negative value will not be
/// changed by this partitioner and will be passed through as is.
///
/// For every message with an "unspecified" `partition` - this is a
/// negative partition - it will try to find one.  In a very simple
/// manner, it tries to distribute such messsages across available
/// partitions in a round robin fashion.
///
/// This behavior may not suffice every workload.  If you're
/// application is dependent on a particular distribution scheme, you
/// want to provide your own partioner to the `Producer` at
/// initialization time.
///
/// See `Builder::with_partitioner`.
pub struct DefaultPartitioner {
    // ~ a counter incremented with each partitioned message to
    // achieve a different partition assignment for each message
    cntr: u32
}

impl Partitioner for DefaultPartitioner {
    #[allow(unused_variables)]
    fn partition(&mut self, topics: Topics, rec: &mut client::ProduceMessage) {
        if rec.partition < 0 {
            match topics.partition_ids(rec.topic) {
                Some(ps) if !ps.is_empty() => {

                    // XXX dispatch to partition by the hash of the key - if that is available, otherwise continue just like below

                    // ~ get a partition
                    rec.partition = ps[self.cntr as usize % ps.len()];
                    // ~ update internal state so that the next time we choose
                    // a different partition
                    self.cntr = self.cntr.wrapping_add(1);
                }
                _ => {}
            }
        }
    }
}

impl Default for DefaultPartitioner {
    fn default() -> Self {
        DefaultPartitioner { cntr: 0 }
    }
}
<|endoftext|>


<commit_before>//! The rbattle game protocol.
//!
//! All hosts participating in an rbattle game have a complete copy of the
//! state, and the algorithm for evolving the state from one moment to the next
//! is strictly deterministic.
//!
//! Every effect the player can have on the game is represented as a
//! `state::Action` value. Given an initial state, and a record of which actions
//! were applied by which players on which turns, you can exactly recreate the
//! progression of the game. Since rbattle is such a simple game, there's only
//! one kind of action: toggling an outflow.
//!
//! In that light, the protocol focuses on gathering user actions, and
//! distributing them out in a way that helps apply them consistently. The
//! protocol has no means for communicating the game state. The only record of
//! the game state transmitted at all is a hash value to detect divergence,
//! which causes the game to end.
//!
//! For simplicity, we designate one host as the server; the protocol doesn't
//! provide for any resilience if the server goes down. All other hosts have TCP
//! connections to the server only.
//!
//! Game play is organized into 'turns', where turns are scheduled at fixed
//! intervals. (We'll aim for 33ms per turn, or 30 turns/second, and see how
//! that goes.) Clients send the server an action list every turn, even if it's
//! an empty action list; and the server broadcasts out the collected action
//! list for every turn, even if it's empty.
//!
//! The server is responsible for coordinating timing. For a given turn duration
//! T, the server broadcasts the list of gathered actions as soon as they are
//! available, but no sooner then T after the last broadcast.
//!
//! Clients should apply received action lists as soon as they are received,
//! advance their state, and send any collected actions immediately.

use map::MapParameters;
use jsonproto::JsonProto;
use scheduler::{CollectedActions, Notifier, PlayerActions, Scheduler};
use state::{Action, Player, SerializableState, State};

use futures::{Future};
use futures::future::ok;
use futures::sync::oneshot;
use serde_json;
use tokio_proto::TcpServer;
use tokio_service::Service;

use std::io::{BufRead, BufReader, BufWriter, Error, ErrorKind, Write};
use std::mem::replace;
use std::net::{SocketAddr, TcpStream};
use std::sync::{Arc, mpsc, Mutex};
use std::thread;

#[derive(Clone)]
struct SchedulerService {
    scheduler: Arc<Mutex<Scheduler>>
}

/// Requests the server receives from clients.
#[derive(Debug, Serialize, Deserialize)]
enum Request {
    Join,
    Actions(PlayerActions),
}

/// The server's responses to those requests.
#[derive(Debug, Serialize, Deserialize)]
enum Response {
    Welcome { player: Player, state: SerializableState },
    GameFull,
    Turn(CollectedActions)
}

/// This impl allows `Scheduler` to resolve promises returned by
/// SchedulerService::call.
impl Notifier for oneshot::Sender<Response> {
    fn notify(self: Box<Self>, turn: CollectedActions) {
        self.send(Response::Turn(turn))
            .expect("oneshot notifier receiver died");
    }
}

/// This impl allows `Scheduler` to send the actions collected for a turn to the
/// local game.
impl Notifier for mpsc::Sender<CollectedActions> {
    fn notify(self: Box<Self>, turn: CollectedActions) {
        self.send(turn)
            .expect("mpsc notifier receiver died");
    }
}

impl Service for SchedulerService {
    type Request = Request;
    type Response = Response;
    type Error = Error;
    type Future = Box<Future<Item=Response, Error=Error>>;

    fn call(&self, req: Request) -> Self::Future {
        match req {
            Request::Join => {
                let mut guard = self.scheduler.lock().unwrap();
                match guard.player_join() {
                    Some((player, state)) =>
                        Box::new(ok(Response::Welcome { player, state })),
                    None =>
                        Box::new(ok(Response::GameFull))
                }
            },
            Request::Actions(actions) => {
                let (sender, receiver) = oneshot::channel();
                let mut guard = self.scheduler.lock().unwrap();
                guard.submit_actions(actions, Box::new(sender));

                // Turn oneshot errors into io::Error, as this service requires.
                let receiver = receiver.map_err(|e| Error::new(ErrorKind::Other, e));

                Box::new(receiver)
            }
        }
    }
}

/// Information shared between the main thread and helper threads.
struct Shared {
    /// The player this state represents. Assigned by the server.
    player: Player,

    /// The current state of the game.
    state: State,

    /// The queue of actions to be sent to the scheduler on the next turn.
    pending: Vec<Action>
}

impl Shared {
    fn apply_collected_actions(&mut self,
                               collected_actions: CollectedActions)
                               -> PlayerActions
    {
        assert_eq!(self.state.turn + 1, collected_actions.turn);

        for action in collected_actions.actions {
            self.state.take_action(&action);
        }
        self.state.advance();

        // We should have applied the same actions to the same state,
        // and gotten the same checksum.
        assert_eq!(self.state.checksum(),
                   collected_actions.state_checksum);

        // Now that we've applied the actions from the prior turn, return
        // whatever actions have been queued up in the mean time as our next
        // turn.
        PlayerActions {
            player: self.player,
            turn: self.state.turn,
            actions: replace(&mut self.pending, vec![])
        }
    }
}

pub struct Participant {
    /// The player on the local machine.
    player: Player,

    /// Information shared between the main thread, the server thread, and the
    /// scheduler thread.
    shared: Arc<Mutex<Shared>>,
}

impl Participant {
    pub fn new_server(addr: SocketAddr, params: MapParameters) -> Participant {
        assert!(params.player_colors.len() >= 1);

        // Create a scheduler to coordinate turns amongst the players,
        // and add ourselves as the first player.
        let mut scheduler = Scheduler::new(State::new(params));
        let (player, current_state) = scheduler.player_join().unwrap();

        let scheduler = Arc::new(Mutex::new(scheduler));

        let shared = Arc::new(Mutex::new(Shared {
            player,
            state: State::from_serializable(current_state),
            pending: vec![]
        }));

        let (sender, receiver): (mpsc::Sender<CollectedActions>, _) = mpsc::channel();

        // Create a thread to apply actions received from the scheduler.
        // These variables get moved into the closure.
        let shared_handle = shared.clone();
        let scheduler_handle = scheduler.clone();
        let sender_handle = sender.clone();
        thread::spawn(move || {
            for collected_actions in receiver {
                let mut guard = shared_handle.lock().unwrap();
                let next_actions = guard.apply_collected_actions(collected_actions);

                // Drop the guard on the shared data first, to avoid having to
                // think about lock ordering.
                drop(guard);

                // Submit any requested next actions for the next turn.
                let mut guard = scheduler_handle.lock().unwrap();
                guard.submit_actions(next_actions, Box::new(sender_handle.clone()));
            }
        });

        // Spawn off a second thread to run the server.
        // This variable gets moved into the closure.
        let scheduler_handle = scheduler.clone();
        thread::spawn(move || {
            let server = TcpServer::new(JsonProto::<Request, Response>::new(), addr);
            server.serve(move || {
                Ok(SchedulerService { scheduler: scheduler_handle.clone() })
            });
        });

        // Get the ball rolling by submitting an empty first move.
        {
            let mut guard = scheduler.lock().unwrap();
            let actions = PlayerActions {
                player,
                turn: 0,
                actions: vec![]
            };
            guard.submit_actions(actions, Box::new(sender));
        }

        Participant { player, shared }
    }

    pub fn new_client(addr: SocketAddr) -> Result<Participant, Error> {
        let stream = TcpStream::connect(addr)?;
        stream.set_nodelay(true)?;

        let (sender, receiver) = mpsc::channel();

        fn setup(reader: &mut BufReader<&TcpStream>, writer: &mut BufWriter<&TcpStream>)
                 -> Result<Shared, Error>
        {
            writeln!(writer, "{}", serde_json::to_string(&Request::Join)?)?;
            writer.flush()?;
            let mut response = String::new();
            reader.read_line(&mut response)?;
            let response = serde_json::from_str(&response)?;
            let (player, state) = match response {
                Response::GameFull => {
                    return Err(Error::new(ErrorKind::Other,
                                          "Connection rejected, game full."));
                }
                Response::Welcome { player, state } => (player, state),
                Response::Turn(_) => {
                    return Err(Error::new(ErrorKind::Other,
                                          "Received unexpected Response::Turn on Join"));
                }
            };

            let shared = Shared {
                player,
                state: State::from_serializable(state),
                pending: vec![]
            };

            // Get the ball rolling by submitting an empty first move.
            let actions = PlayerActions {
                player,
                turn: shared.state.turn,
                actions: vec![]
            };
            writeln!(writer, "{}",
                     serde_json::to_string(&Request::Actions(actions))?)?;
            writer.flush()?;

            Ok(shared)
        }

        // Spawn a thread to read collected actions, apply them to our state,
        // and submit any accumulated actions requested.
        thread::spawn(move || {
            let stream = stream; // take ownership
            let mut reader = BufReader::new(&stream);
            let mut writer = BufWriter::new(&stream);

            let shared = match setup(&mut reader, &mut writer) {
                Err(e) => {
                    sender.send(Err(e)).unwrap();
                    return;
                }
                Ok(shared) => shared
            };

            let player = shared.player;
            let shared = Arc::new(Mutex::new(shared));
            sender.send(Ok((player, shared.clone()))).unwrap();
            drop(sender);

            for line in reader.lines() {
                let line = line.expect("error reading response from server");
                let response: Response = serde_json::from_str(&line)
                    .expect("error parsing response from server");
                let collected_actions = match response {
                    Response::Turn(collected_actions) => collected_actions,
                    otherwise => {
                        panic!("Unexpected response from server: {:?}", otherwise);
                    }
                };

                let mut guard = shared.lock().unwrap();
                let next_actions = guard.apply_collected_actions(collected_actions);

                // Drop the guard on the shared data first, to avoid having to
                // think about lock ordering.
                drop(guard);

                // Submit any requested next actions for the next turn.
                let actions = serde_json::to_string(&Request::Actions(next_actions))
                    .expect("failed to jsonify next actions");
                writeln!(writer, "{}", actions)
                    .expect("Sending next actions to server");
                writer.flush().unwrap();
            }
        });

        let (player, shared) = receiver.recv().unwrap()?;

        Ok(Participant { player, shared })
    }

    /// Return a snapshot of the current state.
    pub fn snapshot(&self) -> State {
        let guard = self.shared.lock().unwrap();
        guard.state.clone()
    }

    /// Return the player number of this SynchronizedState.
    pub fn get_player(&self) -> Player { self.player }

    /// Submit `action` to be performed as soon as possible.
    pub fn request_action(&mut self, action: Action) {
        let mut guard = self.shared.lock().unwrap();
        guard.pending.push(action);
    }
}
<commit_msg>Clarify message about divergent checksums.<commit_after>//! The rbattle game protocol.
//!
//! All hosts participating in an rbattle game have a complete copy of the
//! state, and the algorithm for evolving the state from one moment to the next
//! is strictly deterministic.
//!
//! Every effect the player can have on the game is represented as a
//! `state::Action` value. Given an initial state, and a record of which actions
//! were applied by which players on which turns, you can exactly recreate the
//! progression of the game. Since rbattle is such a simple game, there's only
//! one kind of action: toggling an outflow.
//!
//! In that light, the protocol focuses on gathering user actions, and
//! distributing them out in a way that helps apply them consistently. The
//! protocol has no means for communicating the game state. The only record of
//! the game state transmitted at all is a hash value to detect divergence,
//! which causes the game to end.
//!
//! For simplicity, we designate one host as the server; the protocol doesn't
//! provide for any resilience if the server goes down. All other hosts have TCP
//! connections to the server only.
//!
//! Game play is organized into 'turns', where turns are scheduled at fixed
//! intervals. (We'll aim for 33ms per turn, or 30 turns/second, and see how
//! that goes.) Clients send the server an action list every turn, even if it's
//! an empty action list; and the server broadcasts out the collected action
//! list for every turn, even if it's empty.
//!
//! The server is responsible for coordinating timing. For a given turn duration
//! T, the server broadcasts the list of gathered actions as soon as they are
//! available, but no sooner then T after the last broadcast.
//!
//! Clients should apply received action lists as soon as they are received,
//! advance their state, and send any collected actions immediately.

use map::MapParameters;
use jsonproto::JsonProto;
use scheduler::{CollectedActions, Notifier, PlayerActions, Scheduler};
use state::{Action, Player, SerializableState, State};

use futures::{Future};
use futures::future::ok;
use futures::sync::oneshot;
use serde_json;
use tokio_proto::TcpServer;
use tokio_service::Service;

use std::io::{BufRead, BufReader, BufWriter, Error, ErrorKind, Write};
use std::mem::replace;
use std::net::{SocketAddr, TcpStream};
use std::sync::{Arc, mpsc, Mutex};
use std::thread;

#[derive(Clone)]
struct SchedulerService {
    scheduler: Arc<Mutex<Scheduler>>
}

/// Requests the server receives from clients.
#[derive(Debug, Serialize, Deserialize)]
enum Request {
    Join,
    Actions(PlayerActions),
}

/// The server's responses to those requests.
#[derive(Debug, Serialize, Deserialize)]
enum Response {
    Welcome { player: Player, state: SerializableState },
    GameFull,
    Turn(CollectedActions)
}

/// This impl allows `Scheduler` to resolve promises returned by
/// SchedulerService::call.
impl Notifier for oneshot::Sender<Response> {
    fn notify(self: Box<Self>, turn: CollectedActions) {
        self.send(Response::Turn(turn))
            .expect("oneshot notifier receiver died");
    }
}

/// This impl allows `Scheduler` to send the actions collected for a turn to the
/// local game.
impl Notifier for mpsc::Sender<CollectedActions> {
    fn notify(self: Box<Self>, turn: CollectedActions) {
        self.send(turn)
            .expect("mpsc notifier receiver died");
    }
}

impl Service for SchedulerService {
    type Request = Request;
    type Response = Response;
    type Error = Error;
    type Future = Box<Future<Item=Response, Error=Error>>;

    fn call(&self, req: Request) -> Self::Future {
        match req {
            Request::Join => {
                let mut guard = self.scheduler.lock().unwrap();
                match guard.player_join() {
                    Some((player, state)) =>
                        Box::new(ok(Response::Welcome { player, state })),
                    None =>
                        Box::new(ok(Response::GameFull))
                }
            },
            Request::Actions(actions) => {
                let (sender, receiver) = oneshot::channel();
                let mut guard = self.scheduler.lock().unwrap();
                guard.submit_actions(actions, Box::new(sender));

                // Turn oneshot errors into io::Error, as this service requires.
                let receiver = receiver.map_err(|e| Error::new(ErrorKind::Other, e));

                Box::new(receiver)
            }
        }
    }
}

/// Information shared between the main thread and helper threads.
struct Shared {
    /// The player this state represents. Assigned by the server.
    player: Player,

    /// The current state of the game.
    state: State,

    /// The queue of actions to be sent to the scheduler on the next turn.
    pending: Vec<Action>
}

impl Shared {
    fn apply_collected_actions(&mut self,
                               collected_actions: CollectedActions)
                               -> PlayerActions
    {
        assert_eq!(self.state.turn + 1, collected_actions.turn);

        for action in collected_actions.actions {
            self.state.take_action(&action);
        }
        self.state.advance();

        // We should have applied the same actions to the same state,
        // and gotten the same checksum.
        assert_eq!(self.state.checksum(),
                   collected_actions.state_checksum,
                   "Game state checksums have diverged!");

        // Now that we've applied the actions from the prior turn, return
        // whatever actions have been queued up in the mean time as our next
        // turn.
        PlayerActions {
            player: self.player,
            turn: self.state.turn,
            actions: replace(&mut self.pending, vec![])
        }
    }
}

pub struct Participant {
    /// The player on the local machine.
    player: Player,

    /// Information shared between the main thread, the server thread, and the
    /// scheduler thread.
    shared: Arc<Mutex<Shared>>,
}

impl Participant {
    pub fn new_server(addr: SocketAddr, params: MapParameters) -> Participant {
        assert!(params.player_colors.len() >= 1);

        // Create a scheduler to coordinate turns amongst the players,
        // and add ourselves as the first player.
        let mut scheduler = Scheduler::new(State::new(params));
        let (player, current_state) = scheduler.player_join().unwrap();

        let scheduler = Arc::new(Mutex::new(scheduler));

        let shared = Arc::new(Mutex::new(Shared {
            player,
            state: State::from_serializable(current_state),
            pending: vec![]
        }));

        let (sender, receiver): (mpsc::Sender<CollectedActions>, _) = mpsc::channel();

        // Create a thread to apply actions received from the scheduler.
        // These variables get moved into the closure.
        let shared_handle = shared.clone();
        let scheduler_handle = scheduler.clone();
        let sender_handle = sender.clone();
        thread::spawn(move || {
            for collected_actions in receiver {
                let mut guard = shared_handle.lock().unwrap();
                let next_actions = guard.apply_collected_actions(collected_actions);

                // Drop the guard on the shared data first, to avoid having to
                // think about lock ordering.
                drop(guard);

                // Submit any requested next actions for the next turn.
                let mut guard = scheduler_handle.lock().unwrap();
                guard.submit_actions(next_actions, Box::new(sender_handle.clone()));
            }
        });

        // Spawn off a second thread to run the server.
        // This variable gets moved into the closure.
        let scheduler_handle = scheduler.clone();
        thread::spawn(move || {
            let server = TcpServer::new(JsonProto::<Request, Response>::new(), addr);
            server.serve(move || {
                Ok(SchedulerService { scheduler: scheduler_handle.clone() })
            });
        });

        // Get the ball rolling by submitting an empty first move.
        {
            let mut guard = scheduler.lock().unwrap();
            let actions = PlayerActions {
                player,
                turn: 0,
                actions: vec![]
            };
            guard.submit_actions(actions, Box::new(sender));
        }

        Participant { player, shared }
    }

    pub fn new_client(addr: SocketAddr) -> Result<Participant, Error> {
        let stream = TcpStream::connect(addr)?;
        stream.set_nodelay(true)?;

        let (sender, receiver) = mpsc::channel();

        fn setup(reader: &mut BufReader<&TcpStream>, writer: &mut BufWriter<&TcpStream>)
                 -> Result<Shared, Error>
        {
            writeln!(writer, "{}", serde_json::to_string(&Request::Join)?)?;
            writer.flush()?;
            let mut response = String::new();
            reader.read_line(&mut response)?;
            let response = serde_json::from_str(&response)?;
            let (player, state) = match response {
                Response::GameFull => {
                    return Err(Error::new(ErrorKind::Other,
                                          "Connection rejected, game full."));
                }
                Response::Welcome { player, state } => (player, state),
                Response::Turn(_) => {
                    return Err(Error::new(ErrorKind::Other,
                                          "Received unexpected Response::Turn on Join"));
                }
            };

            let shared = Shared {
                player,
                state: State::from_serializable(state),
                pending: vec![]
            };

            // Get the ball rolling by submitting an empty first move.
            let actions = PlayerActions {
                player,
                turn: shared.state.turn,
                actions: vec![]
            };
            writeln!(writer, "{}",
                     serde_json::to_string(&Request::Actions(actions))?)?;
            writer.flush()?;

            Ok(shared)
        }

        // Spawn a thread to read collected actions, apply them to our state,
        // and submit any accumulated actions requested.
        thread::spawn(move || {
            let stream = stream; // take ownership
            let mut reader = BufReader::new(&stream);
            let mut writer = BufWriter::new(&stream);

            let shared = match setup(&mut reader, &mut writer) {
                Err(e) => {
                    sender.send(Err(e)).unwrap();
                    return;
                }
                Ok(shared) => shared
            };

            let player = shared.player;
            let shared = Arc::new(Mutex::new(shared));
            sender.send(Ok((player, shared.clone()))).unwrap();
            drop(sender);

            for line in reader.lines() {
                let line = line.expect("error reading response from server");
                let response: Response = serde_json::from_str(&line)
                    .expect("error parsing response from server");
                let collected_actions = match response {
                    Response::Turn(collected_actions) => collected_actions,
                    otherwise => {
                        panic!("Unexpected response from server: {:?}", otherwise);
                    }
                };

                let mut guard = shared.lock().unwrap();
                let next_actions = guard.apply_collected_actions(collected_actions);

                // Drop the guard on the shared data first, to avoid having to
                // think about lock ordering.
                drop(guard);

                // Submit any requested next actions for the next turn.
                let actions = serde_json::to_string(&Request::Actions(next_actions))
                    .expect("failed to jsonify next actions");
                writeln!(writer, "{}", actions)
                    .expect("Sending next actions to server");
                writer.flush().unwrap();
            }
        });

        let (player, shared) = receiver.recv().unwrap()?;

        Ok(Participant { player, shared })
    }

    /// Return a snapshot of the current state.
    pub fn snapshot(&self) -> State {
        let guard = self.shared.lock().unwrap();
        guard.state.clone()
    }

    /// Return the player number of this SynchronizedState.
    pub fn get_player(&self) -> Player { self.player }

    /// Submit `action` to be performed as soon as possible.
    pub fn request_action(&mut self, action: Action) {
        let mut guard = self.shared.lock().unwrap();
        guard.pending.push(action);
    }
}
<|endoftext|>


<commit_before>use glm;
use moho::errors::*;
use moho::renderer::Renderer;
use moho::resource_manager::{ResourceManager, Texture};
use sdl2::rect;

pub enum Drawable<'a> {
    Asset(&'a Asset),
    Rectangles(&'a [rect::Rect]),
}

impl<'a> Drawable<'a> {
    pub fn draw<R: Renderer>(&self, renderer: &mut ResourceManager<R>) -> Result<()> {
        match *self {
            Drawable::Asset(ref a) => a.draw(renderer),
            Drawable::Rectangles(ref r) => renderer.fill_rects(r),
        }
    }
}

#[derive(Clone)]
pub struct Asset {
    texture_id: usize,
    pub dst_rect: glm::IVec4,
    pub src_rect: Option<glm::DVec4>,
}

impl Asset {
    pub fn from_texture(texture: &Texture) -> Asset {
        let rect = glm::ivec4(0, 0, texture.dims.x as i32, texture.dims.y as i32);
        Asset::new(texture.id, rect)
    }

    pub fn new(texture_id: usize, dst_rect: glm::IVec4) -> Asset {
        Asset {
            texture_id: texture_id,
            dst_rect: dst_rect,
            src_rect: None,
        }
    }

    pub fn set_center(&mut self, center: glm::IVec2) {
        self.dst_rect.x = center.x - self.dst_rect.z / 2;
        self.dst_rect.y = center.y - self.dst_rect.w / 2;
    }

    pub fn center(&self) -> glm::IVec2 {
        glm::ivec2(self.dst_rect.x + self.dst_rect.z / 2,
                   self.dst_rect.y + self.dst_rect.w / 2)
    }

    pub fn draw<R>(&self, renderer: &mut ResourceManager<R>) -> Result<()>
        where R: Renderer
    {
        let max = renderer.output_size()?;
        renderer.draw(self.texture_id,
                      Some(self.dst_rect),
                      self.src_rect,
                      Some(max))
    }
}
<commit_msg>move draw to drawable instead of asset<commit_after>use glm;
use moho::errors::*;
use moho::renderer::Renderer;
use moho::resource_manager::{ResourceManager, Texture};
use sdl2::rect;

pub enum Drawable<'a> {
    Asset(&'a Asset),
    Rectangles(&'a [rect::Rect]),
}

impl<'a> Drawable<'a> {
    pub fn draw<R: Renderer>(&self, renderer: &mut ResourceManager<R>) -> Result<()> {
        match *self {
            Drawable::Asset(ref a) => {
                let max = Some(renderer.output_size()?);
                renderer.draw(a.texture_id, Some(a.dst_rect), a.src_rect, max)
            }
            Drawable::Rectangles(ref r) => renderer.fill_rects(r),
        }
    }
}

#[derive(Clone)]
pub struct Asset {
    pub texture_id: usize,
    pub dst_rect: glm::IVec4,
    pub src_rect: Option<glm::DVec4>,
}

impl Asset {
    pub fn from_texture(texture: &Texture) -> Asset {
        let rect = glm::ivec4(0, 0, texture.dims.x as i32, texture.dims.y as i32);
        Asset::new(texture.id, rect)
    }

    pub fn new(texture_id: usize, dst_rect: glm::IVec4) -> Asset {
        Asset {
            texture_id: texture_id,
            dst_rect: dst_rect,
            src_rect: None,
        }
    }

    pub fn set_center(&mut self, center: glm::IVec2) {
        self.dst_rect.x = center.x - self.dst_rect.z / 2;
        self.dst_rect.y = center.y - self.dst_rect.w / 2;
    }

    pub fn center(&self) -> glm::IVec2 {
        glm::ivec2(self.dst_rect.x + self.dst_rect.z / 2,
                   self.dst_rect.y + self.dst_rect.w / 2)
    }
}
<|endoftext|>