<commit_before>#![allow(missing_docs)] // FIXME: Document this

pub mod fs;
mod string;
use errors::Error;
use regex::Regex;

use pulldown_cmark::{
    html, Event, Options, Parser, Tag, OPTION_ENABLE_FOOTNOTES, OPTION_ENABLE_TABLES,
};

use std::borrow::Cow;

pub use self::string::{take_lines, RangeArgument};

/// Replaces multiple consecutive whitespace characters with a single space character.
pub fn collapse_whitespace<'a>(text: &'a str) -> Cow<'a, str> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\s\s+").unwrap();
    }
    RE.replace_all(text, " ")
}

/// Convert the given string to a valid HTML element ID
pub fn normalize_id(content: &str) -> String {
    let mut ret = content
        .chars()
        .filter_map(|ch| {
            if ch.is_alphanumeric() || ch == '_' || ch == '-' {
                Some(ch.to_ascii_lowercase())
            } else if ch.is_whitespace() {
                Some('-')
            } else {
                None
            }
        }).collect::<String>();
    // Ensure that the first character is [A-Za-z]
    if ret
        .chars()
        .next()
        .map_or(false, |c| !c.is_ascii_alphabetic())
    {
        ret.insert(0, 'a');
    }
    ret
}

/// Generate an ID for use with anchors which is derived from a "normalised"
/// string.
pub fn id_from_content(content: &str) -> String {
    let mut content = content.to_string();

    // Skip any tags or html-encoded stuff
    const REPL_SUB: &[&str] = &[
        "",
        "",
        "",
        "",
        "",
        "",
        "<",
        ">",
        "&",
        "'",
        """,
    ];
    for sub in REPL_SUB {
        content = content.replace(sub, "");
    }

    // Remove spaces and hashes indicating a header
    let trimmed = content.trim().trim_left_matches('#').trim();

    normalize_id(trimmed)
}

fn adjust_links(event: Event) -> Event {
    lazy_static! {
        static ref HTTP_LINK: Regex = Regex::new("^https?://").unwrap();
        static ref MD_LINK: Regex = Regex::new("(?P<link>.*).md(?P<anchor>#.*)?").unwrap();
    }

    match event {
        Event::Start(Tag::Link(dest, title)) => {
            if !HTTP_LINK.is_match(&dest) {
                if let Some(caps) = MD_LINK.captures(&dest) {
                    let mut html_link = [&caps["link"], ".html"].concat();

                    if let Some(anchor) = caps.name("anchor") {
                        html_link.push_str(anchor.as_str());
                    }

                    return Event::Start(Tag::Link(Cow::from(html_link), title));
                }
            }

            Event::Start(Tag::Link(dest, title))
        }
        _ => event,
    }
}

/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
    let mut s = String::with_capacity(text.len() * 3 / 2);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    let p = Parser::new_ext(text, opts);
    let mut converter = EventQuoteConverter::new(curly_quotes);
    let events = p
        .map(clean_codeblock_headers)
        .map(adjust_links)
        .map(|event| converter.convert(event));

    html::push_html(&mut s, events);
    s
}

struct EventQuoteConverter {
    enabled: bool,
    convert_text: bool,
}

impl EventQuoteConverter {
    fn new(enabled: bool) -> Self {
        EventQuoteConverter {
            enabled: enabled,
            convert_text: true,
        }
    }

    fn convert<'a>(&mut self, event: Event<'a>) -> Event<'a> {
        if !self.enabled {
            return event;
        }

        match event {
            Event::Start(Tag::CodeBlock(_)) | Event::Start(Tag::Code) => {
                self.convert_text = false;
                event
            }
            Event::End(Tag::CodeBlock(_)) | Event::End(Tag::Code) => {
                self.convert_text = true;
                event
            }
            Event::Text(ref text) if self.convert_text => {
                Event::Text(Cow::from(convert_quotes_to_curly(text)))
            }
            _ => event,
        }
    }
}

fn clean_codeblock_headers(event: Event) -> Event {
    match event {
        Event::Start(Tag::CodeBlock(ref info)) => {
            let info: String = info.chars().filter(|ch| !ch.is_whitespace()).collect();

            Event::Start(Tag::CodeBlock(Cow::from(info)))
        }
        _ => event,
    }
}

fn convert_quotes_to_curly(original_text: &str) -> String {
    // We'll consider the start to be "whitespace".
    let mut preceded_by_whitespace = true;

    original_text
        .chars()
        .map(|original_char| {
            let converted_char = match original_char {
                '\'' => {
                    if preceded_by_whitespace {
                        '‘'
                    } else {
                        '’'
                    }
                }
                '"' => {
                    if preceded_by_whitespace {
                        '“'
                    } else {
                        '”'
                    }
                }
                _ => original_char,
            };

            preceded_by_whitespace = original_char.is_whitespace();

            converted_char
        }).collect()
}

/// Prints a "backtrace" of some `Error`.
pub fn log_backtrace(e: &Error) {
    error!("Error: {}", e);

    for cause in e.iter().skip(1) {
        error!("\tCaused By: {}", cause);
    }
}

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

        #[test]
        fn preserves_external_links() {
            assert_eq!(
                render_markdown("[example](https://www.rust-lang.org/)", false),
                "
example
\n"
            );
        }

        #[test]
        fn it_can_adjust_markdown_links() {
            assert_eq!(
                render_markdown("[example](example.md)", false),
                "
example
\n"
            );
            assert_eq!(
                render_markdown("[example_anchor](example.md#anchor)", false),
                "
example_anchor
\n"
            );
        }

        #[test]
        fn it_can_keep_quotes_straight() {
            assert_eq!(render_markdown("'one'", false), "
'one'
\n");
        }

        #[test]
        fn it_can_make_quotes_curly_except_when_they_are_in_code() {
            let input = r#"
'one'
```
'two'
```
`'three'` 'four'"#;
            let expected = r#"
‘one’

'two'

'three' ‘four’

"#;
            assert_eq!(render_markdown(input, true), expected);
        }

        #[test]
        fn whitespace_outside_of_codeblock_header_is_preserved() {
            let input = r#"
some text with spaces
```rust
fn main() {
// code inside is unchanged
}
```
more text with spaces
"#;

            let expected = r#"
some text with spaces

fn main() {
// code inside is unchanged
}

more text with spaces

"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);
        }

        #[test]
        fn rust_code_block_properties_are_passed_as_space_delimited_class() {
            let input = r#"
```rust,no_run,should_panic,property_3
```
"#;

            let expected =
                r#"

"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);
        }

        #[test]
        fn rust_code_block_properties_with_whitespace_are_passed_as_space_delimited_class() {
            let input = r#"
```rust,    no_run,,,should_panic , ,property_3
```
"#;

            let expected =
                r#"

"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);
        }

        #[test]
        fn rust_code_block_without_properties_has_proper_html_class() {
            let input = r#"
```rust
```
"#;

            let expected = r#"

"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);

            let input = r#"
```rust
```
"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);
        }
    }

    mod html_munging {
        use super::super::{id_from_content, normalize_id};

        #[test]
        fn it_generates_anchors() {
            assert_eq!(
                id_from_content("## Method-call expressions"),
                "method-call-expressions"
            );
            assert_eq!(
                id_from_content("## **Bold** title"),
                "bold-title"
            );
            assert_eq!(
                id_from_content("## `Code` title"),
                "code-title"
            );
        }

        #[test]
        fn it_generates_anchors_from_non_ascii_initial() {
            assert_eq!(
                id_from_content("## `--passes`: add more rustdoc passes"),
                "--passes-add-more-rustdoc-passes"
            );
            assert_eq!(
                id_from_content("## 中文標題 CJK title"),
                "中文標題-cjk-title"
            );
            assert_eq!(
                id_from_content("## Über"),
                "Über"
            );
        }

        #[test]
        fn it_normalizes_ids() {
            assert_eq!(
                normalize_id("`--passes`: add more rustdoc passes"),
                "--passes-add-more-rustdoc-passes"
            );
            assert_eq!(
                normalize_id("Method-call 🐙 expressions \u{1f47c}"),
                "method-call--expressions-"
            );
            assert_eq!(normalize_id("_-_12345"), "_-_12345");
            assert_eq!(normalize_id("12345"), "12345");
            assert_eq!(normalize_id("中文"), "中文");
            assert_eq!(normalize_id("にほんご"), "にほんご");
            assert_eq!(normalize_id("한국어"), "한국어");
            assert_eq!(normalize_id(""), "");
        }
    }

    mod convert_quotes_to_curly {
        use super::super::convert_quotes_to_curly;

        #[test]
        fn it_converts_single_quotes() {
            assert_eq!(
                convert_quotes_to_curly("'one', 'two'"),
                "‘one’, ‘two’"
            );
        }

        #[test]
        fn it_converts_double_quotes() {
            assert_eq!(
                convert_quotes_to_curly(r#""one", "two""#),
                "“one”, “two”"
            );
        }

        #[test]
        fn it_treats_tab_as_whitespace() {
            assert_eq!(convert_quotes_to_curly("\t'one'"), "\t‘one’");
        }
    }
}
<commit_msg>Remove insertion on non alphabetic initial headings<commit_after>#![allow(missing_docs)] // FIXME: Document this

pub mod fs;
mod string;
use errors::Error;
use regex::Regex;

use pulldown_cmark::{
    html, Event, Options, Parser, Tag, OPTION_ENABLE_FOOTNOTES, OPTION_ENABLE_TABLES,
};

use std::borrow::Cow;

pub use self::string::{take_lines, RangeArgument};

/// Replaces multiple consecutive whitespace characters with a single space character.
pub fn collapse_whitespace<'a>(text: &'a str) -> Cow<'a, str> {
    lazy_static! {
        static ref RE: Regex = Regex::new(r"\s\s+").unwrap();
    }
    RE.replace_all(text, " ")
}

/// Convert the given string to a valid HTML element ID.
/// The only restriction is that the ID must not contain any ASCII whitespace.
pub fn normalize_id(content: &str) -> String {
    content
        .chars()
        .filter_map(|ch| {
            if ch.is_alphanumeric() || ch == '_' || ch == '-' {
                Some(ch.to_ascii_lowercase())
            } else if ch.is_whitespace() {
                Some('-')
            } else {
                None
            }
        }).collect::<String>()
}

/// Generate an ID for use with anchors which is derived from a "normalised"
/// string.
pub fn id_from_content(content: &str) -> String {
    let mut content = content.to_string();

    // Skip any tags or html-encoded stuff
    const REPL_SUB: &[&str] = &[
        "",
        "",
        "",
        "",
        "",
        "",
        "<",
        ">",
        "&",
        "'",
        """,
    ];
    for sub in REPL_SUB {
        content = content.replace(sub, "");
    }

    // Remove spaces and hashes indicating a header
    let trimmed = content.trim().trim_left_matches('#').trim();

    normalize_id(trimmed)
}

fn adjust_links(event: Event) -> Event {
    lazy_static! {
        static ref HTTP_LINK: Regex = Regex::new("^https?://").unwrap();
        static ref MD_LINK: Regex = Regex::new("(?P<link>.*).md(?P<anchor>#.*)?").unwrap();
    }

    match event {
        Event::Start(Tag::Link(dest, title)) => {
            if !HTTP_LINK.is_match(&dest) {
                if let Some(caps) = MD_LINK.captures(&dest) {
                    let mut html_link = [&caps["link"], ".html"].concat();

                    if let Some(anchor) = caps.name("anchor") {
                        html_link.push_str(anchor.as_str());
                    }

                    return Event::Start(Tag::Link(Cow::from(html_link), title));
                }
            }

            Event::Start(Tag::Link(dest, title))
        }
        _ => event,
    }
}

/// Wrapper around the pulldown-cmark parser for rendering markdown to HTML.
pub fn render_markdown(text: &str, curly_quotes: bool) -> String {
    let mut s = String::with_capacity(text.len() * 3 / 2);

    let mut opts = Options::empty();
    opts.insert(OPTION_ENABLE_TABLES);
    opts.insert(OPTION_ENABLE_FOOTNOTES);

    let p = Parser::new_ext(text, opts);
    let mut converter = EventQuoteConverter::new(curly_quotes);
    let events = p
        .map(clean_codeblock_headers)
        .map(adjust_links)
        .map(|event| converter.convert(event));

    html::push_html(&mut s, events);
    s
}

struct EventQuoteConverter {
    enabled: bool,
    convert_text: bool,
}

impl EventQuoteConverter {
    fn new(enabled: bool) -> Self {
        EventQuoteConverter {
            enabled: enabled,
            convert_text: true,
        }
    }

    fn convert<'a>(&mut self, event: Event<'a>) -> Event<'a> {
        if !self.enabled {
            return event;
        }

        match event {
            Event::Start(Tag::CodeBlock(_)) | Event::Start(Tag::Code) => {
                self.convert_text = false;
                event
            }
            Event::End(Tag::CodeBlock(_)) | Event::End(Tag::Code) => {
                self.convert_text = true;
                event
            }
            Event::Text(ref text) if self.convert_text => {
                Event::Text(Cow::from(convert_quotes_to_curly(text)))
            }
            _ => event,
        }
    }
}

fn clean_codeblock_headers(event: Event) -> Event {
    match event {
        Event::Start(Tag::CodeBlock(ref info)) => {
            let info: String = info.chars().filter(|ch| !ch.is_whitespace()).collect();

            Event::Start(Tag::CodeBlock(Cow::from(info)))
        }
        _ => event,
    }
}

fn convert_quotes_to_curly(original_text: &str) -> String {
    // We'll consider the start to be "whitespace".
    let mut preceded_by_whitespace = true;

    original_text
        .chars()
        .map(|original_char| {
            let converted_char = match original_char {
                '\'' => {
                    if preceded_by_whitespace {
                        '‘'
                    } else {
                        '’'
                    }
                }
                '"' => {
                    if preceded_by_whitespace {
                        '“'
                    } else {
                        '”'
                    }
                }
                _ => original_char,
            };

            preceded_by_whitespace = original_char.is_whitespace();

            converted_char
        }).collect()
}

/// Prints a "backtrace" of some `Error`.
pub fn log_backtrace(e: &Error) {
    error!("Error: {}", e);

    for cause in e.iter().skip(1) {
        error!("\tCaused By: {}", cause);
    }
}

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

        #[test]
        fn preserves_external_links() {
            assert_eq!(
                render_markdown("[example](https://www.rust-lang.org/)", false),
                "
example
\n"
            );
        }

        #[test]
        fn it_can_adjust_markdown_links() {
            assert_eq!(
                render_markdown("[example](example.md)", false),
                "
example
\n"
            );
            assert_eq!(
                render_markdown("[example_anchor](example.md#anchor)", false),
                "
example_anchor
\n"
            );
        }

        #[test]
        fn it_can_keep_quotes_straight() {
            assert_eq!(render_markdown("'one'", false), "
'one'
\n");
        }

        #[test]
        fn it_can_make_quotes_curly_except_when_they_are_in_code() {
            let input = r#"
'one'
```
'two'
```
`'three'` 'four'"#;
            let expected = r#"
‘one’

'two'

'three' ‘four’

"#;
            assert_eq!(render_markdown(input, true), expected);
        }

        #[test]
        fn whitespace_outside_of_codeblock_header_is_preserved() {
            let input = r#"
some text with spaces
```rust
fn main() {
// code inside is unchanged
}
```
more text with spaces
"#;

            let expected = r#"
some text with spaces

fn main() {
// code inside is unchanged
}

more text with spaces

"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);
        }

        #[test]
        fn rust_code_block_properties_are_passed_as_space_delimited_class() {
            let input = r#"
```rust,no_run,should_panic,property_3
```
"#;

            let expected =
                r#"

"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);
        }

        #[test]
        fn rust_code_block_properties_with_whitespace_are_passed_as_space_delimited_class() {
            let input = r#"
```rust,    no_run,,,should_panic , ,property_3
```
"#;

            let expected =
                r#"

"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);
        }

        #[test]
        fn rust_code_block_without_properties_has_proper_html_class() {
            let input = r#"
```rust
```
"#;

            let expected = r#"

"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);

            let input = r#"
```rust
```
"#;
            assert_eq!(render_markdown(input, false), expected);
            assert_eq!(render_markdown(input, true), expected);
        }
    }

    mod html_munging {
        use super::super::{id_from_content, normalize_id};

        #[test]
        fn it_generates_anchors() {
            assert_eq!(
                id_from_content("## Method-call expressions"),
                "method-call-expressions"
            );
            assert_eq!(
                id_from_content("## **Bold** title"),
                "bold-title"
            );
            assert_eq!(
                id_from_content("## `Code` title"),
                "code-title"
            );
        }

        #[test]
        fn it_generates_anchors_from_non_ascii_initial() {
            assert_eq!(
                id_from_content("## `--passes`: add more rustdoc passes"),
                "--passes-add-more-rustdoc-passes"
            );
            assert_eq!(
                id_from_content("## 中文標題 CJK title"),
                "中文標題-cjk-title"
            );
            assert_eq!(
                id_from_content("## Über"),
                "Über"
            );
        }

        #[test]
        fn it_normalizes_ids() {
            assert_eq!(
                normalize_id("`--passes`: add more rustdoc passes"),
                "--passes-add-more-rustdoc-passes"
            );
            assert_eq!(
                normalize_id("Method-call 🐙 expressions \u{1f47c}"),
                "method-call--expressions-"
            );
            assert_eq!(normalize_id("_-_12345"), "_-_12345");
            assert_eq!(normalize_id("12345"), "12345");
            assert_eq!(normalize_id("中文"), "中文");
            assert_eq!(normalize_id("にほんご"), "にほんご");
            assert_eq!(normalize_id("한국어"), "한국어");
            assert_eq!(normalize_id(""), "");
        }
    }

    mod convert_quotes_to_curly {
        use super::super::convert_quotes_to_curly;

        #[test]
        fn it_converts_single_quotes() {
            assert_eq!(
                convert_quotes_to_curly("'one', 'two'"),
                "‘one’, ‘two’"
            );
        }

        #[test]
        fn it_converts_double_quotes() {
            assert_eq!(
                convert_quotes_to_curly(r#""one", "two""#),
                "“one”, “two”"
            );
        }

        #[test]
        fn it_treats_tab_as_whitespace() {
            assert_eq!(convert_quotes_to_curly("\t'one'"), "\t‘one’");
        }
    }
}
<|endoftext|>


<commit_before>//! Module for holding Validated logic
//!
//! `Validated` is a way of running a bunch of operations that can go wrong (for example,
//! functions returning `Result<T, E>`) and, in the case of one or more things going wrong,
//! having all the errors returned to you all at once. In the case that everything went well, you get
//! an `HList` of all your results.
//!
//! ```
//! # #[macro_use] extern crate frunk;
//! # use frunk::hlist::*;
//! # use frunk::validated::*;
//! # fn main() {
//!
//! #[derive(PartialEq, Eq, Debug)]
//! struct Person {
//!     age: i32,
//!     name: String,
//! }
//!
//! fn get_name() -> Result<String, String> {
//!     Result::Ok("James".to_owned())
//! }
//!
//! fn get_age() -> Result<i32, String> {
//!     Result::Ok(32)
//! }
//!
//! let v: Validated<Hlist!(String, i32), String> = get_name().into_validated() + get_age();
//! let person = v.into_result()
//!                .map(|hlist| {
//!                     let (name, age) = hlist.into_tuple2();
//!                     Person {
//!                         name: name,
//!                         age: age,
//!                     }
//!                 });
//!
//!  assert_eq!(person.unwrap(),
//!             Person {
//!                 name: "James".to_owned(),
//!                 age: 32,
//!             });
//! # }
//! ```

use super::hlist::*;
use std::ops::Add;

/// A Validated is either an Ok holding an HList or an Err, holding a vector
/// of collected errors.
#[derive(PartialEq, Debug, Eq, Clone, PartialOrd, Ord)]
pub enum Validated<T, E>
    where T: HList
{
    Ok(T),
    Err(Vec<E>),
}

impl<T, E> Validated<T, E>
    where T: HList
{
    /// Returns true if this validation is Ok, false otherwise
    ///
    /// ```
    /// # use frunk::validated::*;
    ///
    /// let r1: Result<String, String> = Result::Ok(String::from("hello"));
    /// let v = r1.into_validated();
    /// assert!(v.is_ok());
    /// ```
    pub fn is_ok(&self) -> bool {
        match *self {
            Validated::Ok(_) => true,
            _ => false,
        }
    }

    /// Returns true if this validation is Err, false otherwise
    ///
    /// ```
    /// # use frunk::validated::*;
    ///
    /// let r1: Result<String, i32> = Result::Err(32);
    /// let v = r1.into_validated();
    /// assert!(v.is_err());
    /// ```
    pub fn is_err(&self) -> bool {
        !self.is_ok()
    }

    /// Turns this Validated into a Result.
    ///
    /// If this Validated is Ok, it will become a Result::Ok, holding an HList of all the accumulated
    /// results. Otherwise, it will become a Result::Err with a list of all accumulated errors.
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; use frunk::validated::*; fn main() {
    ///
    /// #[derive(PartialEq, Eq, Debug)]
    /// struct Person {
    ///     age: i32,
    ///     name: String,
    /// }
    ///
    /// fn get_name() -> Result<String, String> {
    ///     Result::Ok("James".to_owned())
    /// }
    ///
    /// fn get_age() -> Result<i32, String> {
    ///     Result::Ok(32)
    /// }
    ///
    /// let v = get_name().into_validated() + get_age();
    /// let person = v.into_result()
    ///                .map(|hlist| {
    ///                     let (name, age) = hlist.into_tuple2();
    ///                     Person {
    ///                         name: name,
    ///                         age: age,
    ///                     }
    ///                 });
    ///
    ///  assert_eq!(person.unwrap(),
    ///             Person {
    ///                 name: "James".to_owned(),
    ///                 age: 32,
    ///             });
    /// # }
    pub fn into_result(self) -> Result<T, Vec<E>> {
        match self {
            Validated::Ok(h) => Result::Ok(h),
            Validated::Err(errors) => Result::Err(errors),
        }
    }
}

/// Trait for "lifting" a given type into a Validated
pub trait IntoValidated<T, E> {
    /// Consumes the current Result into a Validated so that we can begin chaining
    ///
    /// ```
    /// # use frunk::validated::*;
    ///
    /// let r1: Result<String, i32> = Result::Err(32);
    /// let v = r1.into_validated();
    /// assert!(v.is_err());
    /// ```
    fn into_validated(self) -> Validated<HCons<T, HNil>, E>;
}

impl<T, E> IntoValidated<T, E> for Result<T, E> {
    fn into_validated(self) -> Validated<HCons<T, HNil>, E> {
        match self {
            Result::Err(e) => Validated::Err(vec![e]),
            Result::Ok(v) => {
                Validated::Ok(HCons {
                    head: v,
                    tail: HNil,
                })
            }
        }
    }
}

/// Implements Add for the current Validated with a Result, returning a new Validated.
///
/// ```
/// # #[macro_use] extern crate frunk; use frunk::hlist::*; use frunk::validated::*; fn main() {
///
/// let r1: Result<String, String> = Result::Ok(String::from("hello"));
/// let r2: Result<i32, String> = Result::Ok(1);
/// let v = r1.into_validated() + r2;
/// assert_eq!(v, Validated::Ok(hlist!(String::from("hello"), 1)))
/// # }
/// ```
///
impl<T, E, T2> Add<Result<T2, E>> for Validated<T, E>
    where T: HList + Add<HCons<T2, HNil>>,
          <T as Add<HCons<T2, HNil>>>::Output: HList
{
    type Output = Validated<<T as Add<HCons<T2, HNil>>>::Output, E>;

    fn add(self, other: Result<T2, E>) -> Self::Output {
        let other_as_validated = other.into_validated();
        self + other_as_validated
    }
}

/// Implements Add for the current Validated with another Validated, returning a new Validated.
///
/// ```
/// # #[macro_use] extern crate frunk; use frunk::hlist::*; use frunk::validated::*; fn main() {
/// let r1: Result<String, String> = Result::Ok(String::from("hello"));
/// let r2: Result<i32, String> = Result::Ok(1);
/// let v1 = r1.into_validated();
/// let v2 = r2.into_validated();
/// let v3 = v1 + v2;
/// assert_eq!(v3, Validated::Ok(hlist!(String::from("hello"), 1)))
/// # }
/// ```
impl<T, E, T2> Add<Validated<T2, E>> for Validated<T, E>
    where T: HList + Add<T2>,
          T2: HList,
          <T as Add<T2>>::Output: HList
{
    type Output = Validated<<T as Add<T2>>::Output, E>;

    fn add(self, other: Validated<T2, E>) -> Self::Output {
        match (self, other) {
            (Validated::Err(mut errs), Validated::Err(errs2)) => {
                errs.extend(errs2);
                Validated::Err(errs)
            }
            (Validated::Err(errs), _) => Validated::Err(errs),
            (_, Validated::Err(errs)) => Validated::Err(errs),
            (Validated::Ok(h1), Validated::Ok(h2)) => Validated::Ok(h1 + h2),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::super::hlist::*;
    use super::*;

    #[test]
    fn test_adding_ok_results() {
        let r1: Result<String, String> = Result::Ok(String::from("hello"));
        let r2: Result<i32, String> = Result::Ok(1);
        let v = r1.into_validated() + r2;
        assert_eq!(v, Validated::Ok(hlist!(String::from("hello"), 1)))
    }

    #[test]
    fn test_adding_validated_oks() {
        let r1: Result<String, String> = Result::Ok(String::from("hello"));
        let r2: Result<i32, String> = Result::Ok(1);
        let r3: Result<i32, String> = Result::Ok(3);
        let v1 = r1.into_validated();
        let v2 = r2.into_validated();
        let v3 = r3.into_validated();
        let comb = v1 + v2 + v3;
        assert_eq!(comb, Validated::Ok(hlist!(String::from("hello"), 1, 3)))
    }

    #[test]
    fn test_adding_err_results() {
        let r1: Result<i16, String> = Result::Ok(1);
        let r2: Result<i16, String> = Result::Err(String::from("NO!"));
        let v1 = r1.into_validated() + r2;
        assert!(v1.is_err());
        assert_eq!(v1, Validated::Err(vec!["NO!".to_owned()]))
    }

    #[derive(PartialEq, Eq, Debug)]
    struct Person {
        age: i32,
        name: String,
        email: String,
    }

    #[derive(PartialEq, Eq, Debug)]
    pub enum YahNah {
        Yah,
        Nah,
    }

    /// Our Errors
    #[derive(PartialEq, Eq, Debug)]
    pub enum Nope {
        NameNope,
        AgeNope,
        EmailNope,
    }

    fn get_name(yah_nah: YahNah) -> Result<String, Nope> {
        match yah_nah {
            YahNah::Yah => Result::Ok("James".to_owned()),
            _ => Result::Err(Nope::NameNope),
        }
    }

    fn get_age(yah_nah: YahNah) -> Result<i32, Nope> {
        match yah_nah {
            YahNah::Yah => Result::Ok(32),
            _ => Result::Err(Nope::AgeNope),
        }
    }

    fn get_email(yah_nah: YahNah) -> Result<String, Nope> {
        match yah_nah {
            YahNah::Yah => Result::Ok("hello@world.com".to_owned()),
            _ => Result::Err(Nope::EmailNope),
        }
    }

    #[test]
    fn test_to_result_ok() {
        let v = get_name(YahNah::Yah).into_validated() + get_age(YahNah::Yah) +
                get_email(YahNah::Yah);
        let person = v.into_result()
                      .map(|hlist_pat!(name, age, email)| {
                          Person {
                              name: name,
                              age: age,
                              email: email,
                          }
                      });

        assert_eq!(person.unwrap(),
                   Person {
                       name: "James".to_owned(),
                       age: 32,
                       email: "hello@world.com".to_owned(),
                   });
    }

    #[test]
    fn test_to_result_all_faulty() {
        let v = get_name(YahNah::Nah).into_validated() + get_age(YahNah::Nah) +
                get_email(YahNah::Nah);
        let person = v.into_result()
                      .map(|_| unimplemented!());

        assert_eq!(person.unwrap_err(),
                   vec![Nope::NameNope, Nope::AgeNope, Nope::EmailNope]);
    }

    #[test]
    fn test_to_result_one_faulty() {
        let v = get_name(YahNah::Nah).into_validated() + get_age(YahNah::Yah) +
                get_email(YahNah::Nah);
        let person = v.into_result()
                      .map(|_| unimplemented!());

        assert_eq!(person.unwrap_err(), vec![Nope::NameNope, Nope::EmailNope]);
    }

}
<commit_msg>Update docs<commit_after>//! Module for holding Validated logic
//!
//! `Validated` is a way of running a bunch of operations that can go wrong (for example,
//! functions returning `Result<T, E>`) and, in the case of one or more things going wrong,
//! having all the errors returned to you all at once. In the case that everything went well, you get
//! an `HList` of all your results.
//!
//! ```
//! # #[macro_use] extern crate frunk;
//! # use frunk::hlist::*;
//! # use frunk::validated::*;
//! # fn main() {
//!
//! #[derive(PartialEq, Eq, Debug)]
//! struct Person {
//!     age: i32,
//!     name: String,
//! }
//!
//! fn get_name() -> Result<String, String> {
//!     Result::Ok("James".to_owned())
//! }
//!
//! fn get_age() -> Result<i32, String> {
//!     Result::Ok(32)
//! }
//!
//! let v: Validated<Hlist!(String, i32), String> = get_name().into_validated() + get_age();
//! let person = v.into_result()
//!                .map(|hlist_pat!(name, age)| {
//!                     Person {
//!                         name: name,
//!                         age: age,
//!                     }
//!                 });
//!
//!  assert_eq!(person.unwrap(),
//!             Person {
//!                 name: "James".to_owned(),
//!                 age: 32,
//!             });
//! # }
//! ```

use super::hlist::*;
use std::ops::Add;

/// A Validated is either an Ok holding an HList or an Err, holding a vector
/// of collected errors.
#[derive(PartialEq, Debug, Eq, Clone, PartialOrd, Ord)]
pub enum Validated<T, E>
    where T: HList
{
    Ok(T),
    Err(Vec<E>),
}

impl<T, E> Validated<T, E>
    where T: HList
{
    /// Returns true if this validation is Ok, false otherwise
    ///
    /// ```
    /// # use frunk::validated::*;
    ///
    /// let r1: Result<String, String> = Result::Ok(String::from("hello"));
    /// let v = r1.into_validated();
    /// assert!(v.is_ok());
    /// ```
    pub fn is_ok(&self) -> bool {
        match *self {
            Validated::Ok(_) => true,
            _ => false,
        }
    }

    /// Returns true if this validation is Err, false otherwise
    ///
    /// ```
    /// # use frunk::validated::*;
    ///
    /// let r1: Result<String, i32> = Result::Err(32);
    /// let v = r1.into_validated();
    /// assert!(v.is_err());
    /// ```
    pub fn is_err(&self) -> bool {
        !self.is_ok()
    }

    /// Turns this Validated into a Result.
    ///
    /// If this Validated is Ok, it will become a Result::Ok, holding an HList of all the accumulated
    /// results. Otherwise, it will become a Result::Err with a list of all accumulated errors.
    ///
    /// ```
    /// # #[macro_use] extern crate frunk; use frunk::hlist::*; use frunk::validated::*; fn main() {
    ///
    /// #[derive(PartialEq, Eq, Debug)]
    /// struct Person {
    ///     age: i32,
    ///     name: String,
    /// }
    ///
    /// fn get_name() -> Result<String, String> {
    ///     Result::Ok("James".to_owned())
    /// }
    ///
    /// fn get_age() -> Result<i32, String> {
    ///     Result::Ok(32)
    /// }
    ///
    /// let v = get_name().into_validated() + get_age();
    /// let person = v.into_result()
    ///                .map(|hlist| {
    ///                     let (name, age) = hlist.into_tuple2();
    ///                     Person {
    ///                         name: name,
    ///                         age: age,
    ///                     }
    ///                 });
    ///
    ///  assert_eq!(person.unwrap(),
    ///             Person {
    ///                 name: "James".to_owned(),
    ///                 age: 32,
    ///             });
    /// # }
    pub fn into_result(self) -> Result<T, Vec<E>> {
        match self {
            Validated::Ok(h) => Result::Ok(h),
            Validated::Err(errors) => Result::Err(errors),
        }
    }
}

/// Trait for "lifting" a given type into a Validated
pub trait IntoValidated<T, E> {
    /// Consumes the current Result into a Validated so that we can begin chaining
    ///
    /// ```
    /// # use frunk::validated::*;
    ///
    /// let r1: Result<String, i32> = Result::Err(32);
    /// let v = r1.into_validated();
    /// assert!(v.is_err());
    /// ```
    fn into_validated(self) -> Validated<HCons<T, HNil>, E>;
}

impl<T, E> IntoValidated<T, E> for Result<T, E> {
    fn into_validated(self) -> Validated<HCons<T, HNil>, E> {
        match self {
            Result::Err(e) => Validated::Err(vec![e]),
            Result::Ok(v) => {
                Validated::Ok(HCons {
                    head: v,
                    tail: HNil,
                })
            }
        }
    }
}

/// Implements Add for the current Validated with a Result, returning a new Validated.
///
/// ```
/// # #[macro_use] extern crate frunk; use frunk::hlist::*; use frunk::validated::*; fn main() {
///
/// let r1: Result<String, String> = Result::Ok(String::from("hello"));
/// let r2: Result<i32, String> = Result::Ok(1);
/// let v = r1.into_validated() + r2;
/// assert_eq!(v, Validated::Ok(hlist!(String::from("hello"), 1)))
/// # }
/// ```
///
impl<T, E, T2> Add<Result<T2, E>> for Validated<T, E>
    where T: HList + Add<HCons<T2, HNil>>,
          <T as Add<HCons<T2, HNil>>>::Output: HList
{
    type Output = Validated<<T as Add<HCons<T2, HNil>>>::Output, E>;

    fn add(self, other: Result<T2, E>) -> Self::Output {
        let other_as_validated = other.into_validated();
        self + other_as_validated
    }
}

/// Implements Add for the current Validated with another Validated, returning a new Validated.
///
/// ```
/// # #[macro_use] extern crate frunk; use frunk::hlist::*; use frunk::validated::*; fn main() {
/// let r1: Result<String, String> = Result::Ok(String::from("hello"));
/// let r2: Result<i32, String> = Result::Ok(1);
/// let v1 = r1.into_validated();
/// let v2 = r2.into_validated();
/// let v3 = v1 + v2;
/// assert_eq!(v3, Validated::Ok(hlist!(String::from("hello"), 1)))
/// # }
/// ```
impl<T, E, T2> Add<Validated<T2, E>> for Validated<T, E>
    where T: HList + Add<T2>,
          T2: HList,
          <T as Add<T2>>::Output: HList
{
    type Output = Validated<<T as Add<T2>>::Output, E>;

    fn add(self, other: Validated<T2, E>) -> Self::Output {
        match (self, other) {
            (Validated::Err(mut errs), Validated::Err(errs2)) => {
                errs.extend(errs2);
                Validated::Err(errs)
            }
            (Validated::Err(errs), _) => Validated::Err(errs),
            (_, Validated::Err(errs)) => Validated::Err(errs),
            (Validated::Ok(h1), Validated::Ok(h2)) => Validated::Ok(h1 + h2),
        }
    }
}

#[cfg(test)]
mod tests {

    use super::super::hlist::*;
    use super::*;

    #[test]
    fn test_adding_ok_results() {
        let r1: Result<String, String> = Result::Ok(String::from("hello"));
        let r2: Result<i32, String> = Result::Ok(1);
        let v = r1.into_validated() + r2;
        assert_eq!(v, Validated::Ok(hlist!(String::from("hello"), 1)))
    }

    #[test]
    fn test_adding_validated_oks() {
        let r1: Result<String, String> = Result::Ok(String::from("hello"));
        let r2: Result<i32, String> = Result::Ok(1);
        let r3: Result<i32, String> = Result::Ok(3);
        let v1 = r1.into_validated();
        let v2 = r2.into_validated();
        let v3 = r3.into_validated();
        let comb = v1 + v2 + v3;
        assert_eq!(comb, Validated::Ok(hlist!(String::from("hello"), 1, 3)))
    }

    #[test]
    fn test_adding_err_results() {
        let r1: Result<i16, String> = Result::Ok(1);
        let r2: Result<i16, String> = Result::Err(String::from("NO!"));
        let v1 = r1.into_validated() + r2;
        assert!(v1.is_err());
        assert_eq!(v1, Validated::Err(vec!["NO!".to_owned()]))
    }

    #[derive(PartialEq, Eq, Debug)]
    struct Person {
        age: i32,
        name: String,
        email: String,
    }

    #[derive(PartialEq, Eq, Debug)]
    pub enum YahNah {
        Yah,
        Nah,
    }

    /// Our Errors
    #[derive(PartialEq, Eq, Debug)]
    pub enum Nope {
        NameNope,
        AgeNope,
        EmailNope,
    }

    fn get_name(yah_nah: YahNah) -> Result<String, Nope> {
        match yah_nah {
            YahNah::Yah => Result::Ok("James".to_owned()),
            _ => Result::Err(Nope::NameNope),
        }
    }

    fn get_age(yah_nah: YahNah) -> Result<i32, Nope> {
        match yah_nah {
            YahNah::Yah => Result::Ok(32),
            _ => Result::Err(Nope::AgeNope),
        }
    }

    fn get_email(yah_nah: YahNah) -> Result<String, Nope> {
        match yah_nah {
            YahNah::Yah => Result::Ok("hello@world.com".to_owned()),
            _ => Result::Err(Nope::EmailNope),
        }
    }

    #[test]
    fn test_to_result_ok() {
        let v = get_name(YahNah::Yah).into_validated() + get_age(YahNah::Yah) +
                get_email(YahNah::Yah);
        let person = v.into_result()
                      .map(|hlist_pat!(name, age, email)| {
                          Person {
                              name: name,
                              age: age,
                              email: email,
                          }
                      });

        assert_eq!(person.unwrap(),
                   Person {
                       name: "James".to_owned(),
                       age: 32,
                       email: "hello@world.com".to_owned(),
                   });
    }

    #[test]
    fn test_to_result_all_faulty() {
        let v = get_name(YahNah::Nah).into_validated() + get_age(YahNah::Nah) +
                get_email(YahNah::Nah);
        let person = v.into_result()
                      .map(|_| unimplemented!());

        assert_eq!(person.unwrap_err(),
                   vec![Nope::NameNope, Nope::AgeNope, Nope::EmailNope]);
    }

    #[test]
    fn test_to_result_one_faulty() {
        let v = get_name(YahNah::Nah).into_validated() + get_age(YahNah::Yah) +
                get_email(YahNah::Nah);
        let person = v.into_result()
                      .map(|_| unimplemented!());

        assert_eq!(person.unwrap_err(), vec![Nope::NameNope, Nope::EmailNope]);
    }

}
<|endoftext|>


<commit_before>use game::{Game, View, ViewAction};
use game::data::Rectangle;
use game::gfx::{CopySprite, Sprite};
use game::constants::{TILES_PCS_W, TILES_PCS_H, PLAYER_SPEED, ZOOM_SPEED, FIRE_SPRITE_START_INDEX};
use views::tilemap::{TerrainTile, TerrainSpriteSheet, get_tiles, viewport_move};
use views::background::{Background};
use views::character::{Character};
use views::zombie::{Zombie};
use data::{load_character};
use sdl2::mixer::{Chunk};
use std::path::Path;

mod character;
mod zombie;
mod tilemap;
mod background;

#[derive(Clone, Copy)]
pub enum Orientation {
  Right = 0,
  UpRight = 1,
  Up = 2,
  UpLeft = 3,
  Left = 4,
  DownLeft = 5,
  Down = 6,
  DownRight = 7,
}

pub struct GameView {
  player: Character,
  tiles: Vec<TerrainTile>,
  sprite_sheet: Vec<Sprite>,
  background: Background,
  zombie: Zombie,
  pistol: Chunk,
}

impl GameView {
  pub fn new(game: &mut Game) -> GameView {
    let pistol_audio_path = "assets/audio/pistol.ogg";
    let character_spritesheet = Sprite::load(&mut game.renderer, "assets/character.png").unwrap();
    let pistol_audio = match Chunk::from_file(Path::new(pistol_audio_path)) {
      Ok(f) => f,
      Err(e) => panic!("File {} not found: {}", pistol_audio_path, e),
    };
    let character_datapoints = load_character();
    let mut character_sprites = Vec::with_capacity(512);

    for x in 0..(FIRE_SPRITE_START_INDEX - 1) {
      character_sprites.push(character_spritesheet.region(character_datapoints[x]).unwrap());
    }

    for x in FIRE_SPRITE_START_INDEX..255 {
      character_sprites.push(character_spritesheet.region(character_datapoints[x]).unwrap());
    }

    GameView {
      player: Character::new(character_sprites),
      tiles: get_tiles(),
      sprite_sheet: TerrainSpriteSheet::new(&game),
      pistol: pistol_audio,
      zombie: Zombie::new(&mut game.renderer),
      background: Background {
        pos: 0.0,
        sprite: Sprite::load(&mut game.renderer, "assets/background.png").unwrap(),
      }
    }
  }
}

impl View for GameView {
  fn render(&mut self, game: &mut Game, elapsed: f64) -> ViewAction {
    if game.events.now.quit || game.events.now.key_escape == Some(true) {
      return ViewAction::Quit;
    }

    let diagonal = (game.events.key_up ^ game.events.key_down) && (game.events.key_left ^ game.events.key_right);
    let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } * PLAYER_SPEED * elapsed;
    let dx = match (game.events.key_left, game.events.key_right) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved * 1.5,
      (false, true) => moved * 1.5,
    };

    let dy = match (game.events.key_up, game.events.key_down) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved * 0.75,
      (false, true) => moved * 0.75,
    };

    self.player.rect.x += dx;
    self.player.rect.y += dy;

    let movable_region = Rectangle {
      x: 0.0,
      y: 0.0,
      w: game.output_size().0 * 2.0,
      h: game.output_size().1 * 2.0,
    };

    let curr_rect = game.renderer.viewport();
    let rect = viewport_move(&game, curr_rect, dx, dy);
    game.renderer.set_viewport(rect.to_sdl());

    self.background.render(&mut game.renderer);

    self.player.rect = self.player.rect.move_inside(movable_region).unwrap();
    self.player.current =
    if dx == 0.0 && dy < 0.0       { Orientation::Up }
    else if dx > 0.0 && dy < 0.0   { Orientation::UpRight }
    else if dx < 0.0 && dy < 0.0   { Orientation::UpLeft }
    else if dx == 0.0 && dy == 0.0 { self.player.heading }
    else if dx > 0.0 && dy == 0.0  { Orientation::Right }
    else if dx < 0.0 && dy == 0.0  { Orientation::Left }
    else if dx == 0.0 && dy > 0.0  { Orientation::Down }
    else if dx > 0.0 && dy > 0.0   { Orientation::DownRight }
    else if dx < 0.0 && dy > 0.0   { Orientation::DownLeft }
    else { unreachable!() };

    self.player.heading = self.player.current;
    for x in 0..TILES_PCS_W {
      for y in 0..TILES_PCS_H {
        let index = x * TILES_PCS_H + y;
        game.renderer.copy_sprite(&self.sprite_sheet[(self.tiles[index].current-1) as usize], self.tiles[index].rect);
      }
    }
    self.zombie.update(elapsed);
    self.zombie.render(&mut game.renderer);

    match game.events.mouse_click {
      Some(_) => {
        let index = 211 + self.player.current as usize * 5 + self.player.fire_anim_index as usize;
        game.renderer.copy_sprite(&self.player.sprites[index], self.player.rect);
        self.player.fire_anim_index =
          if self.player.fire_anim_index < 4u32 { self.player.fire_anim_index + 1u32 } else { 0u32 };
        if self.player.fire_anim_index == 0 {
          game.play_sound(&self.pistol);
        }
      },
      None => {
        let index = self.player.current as usize * 28 + self.player.move_anim_index as usize;
        game.renderer.copy_sprite(&self.player.sprites[index], self.player.rect);
        self.player.move_anim_index =
          if dx == 0.0 && dy == 0.0 { 0u32 } else if self.player.move_anim_index < 13u32 { self.player.move_anim_index + 1u32 } else { 0u32 };
      },
    };

    let scale = game.renderer.scale();
    if game.events.zoom_in == true && scale.0 <= 2.0 && scale.1 <= 2.0 {
      let _ = game.renderer.set_scale(scale.0 + ZOOM_SPEED, scale.1 + ZOOM_SPEED);
    } else if game.events.zoom_out == true && scale.0 > 0.85 && scale.1 > 0.85 {
      let _ = game.renderer.set_scale(scale.0 - ZOOM_SPEED, scale.1 - ZOOM_SPEED);
    }

    ViewAction::None
  }
}
<commit_msg>Implement debug display for 'Orientation' enum<commit_after>use std::fmt::{Display, Formatter, Result};
use game::{Game, View, ViewAction};
use game::data::Rectangle;
use game::gfx::{CopySprite, Sprite};
use game::constants::{TILES_PCS_W, TILES_PCS_H, PLAYER_SPEED, ZOOM_SPEED, FIRE_SPRITE_START_INDEX};
use views::tilemap::{TerrainTile, TerrainSpriteSheet, get_tiles, viewport_move};
use views::background::{Background};
use views::character::{Character};
use views::zombie::{Zombie};
use data::{load_character};
use sdl2::mixer::{Chunk};
use std::path::Path;

mod character;
mod zombie;
mod tilemap;
mod background;

#[derive(Clone, Copy, Debug)]
pub enum Orientation {
  Right = 0,
  UpRight = 1,
  Up = 2,
  UpLeft = 3,
  Left = 4,
  DownLeft = 5,
  Down = 6,
  DownRight = 7,
}

impl Display for Orientation {
  fn fmt(&self, f: &mut Formatter) -> Result {
    use views::Orientation::*;
    match *self {
      Right => write!(f, "0"),
      UpRight => write!(f, "1"),
      Up => write!(f, "2"),
      UpLeft => write!(f, "3"),
      Left => write!(f, "4"),
      DownLeft => write!(f, "5"),
      Down => write!(f, "6"),
      DownRight => write!(f, "7"),
    }
  }
}

pub struct GameView {
  player: Character,
  tiles: Vec<TerrainTile>,
  sprite_sheet: Vec<Sprite>,
  background: Background,
  zombie: Zombie,
  pistol: Chunk,
}

impl GameView {
  pub fn new(game: &mut Game) -> GameView {
    let pistol_audio_path = "assets/audio/pistol.ogg";
    let character_spritesheet = Sprite::load(&mut game.renderer, "assets/character.png").unwrap();
    let pistol_audio = match Chunk::from_file(Path::new(pistol_audio_path)) {
      Ok(f) => f,
      Err(e) => panic!("File {} not found: {}", pistol_audio_path, e),
    };
    let character_datapoints = load_character();
    let mut character_sprites = Vec::with_capacity(512);

    for x in 0..(FIRE_SPRITE_START_INDEX - 1) {
      character_sprites.push(character_spritesheet.region(character_datapoints[x]).unwrap());
    }

    for x in FIRE_SPRITE_START_INDEX..255 {
      character_sprites.push(character_spritesheet.region(character_datapoints[x]).unwrap());
    }

    GameView {
      player: Character::new(character_sprites),
      tiles: get_tiles(),
      sprite_sheet: TerrainSpriteSheet::new(&game),
      pistol: pistol_audio,
      zombie: Zombie::new(&mut game.renderer),
      background: Background {
        pos: 0.0,
        sprite: Sprite::load(&mut game.renderer, "assets/background.png").unwrap(),
      }
    }
  }
}

impl View for GameView {
  fn render(&mut self, game: &mut Game, elapsed: f64) -> ViewAction {
    if game.events.now.quit || game.events.now.key_escape == Some(true) {
      return ViewAction::Quit;
    }

    let diagonal = (game.events.key_up ^ game.events.key_down) && (game.events.key_left ^ game.events.key_right);
    let moved = if diagonal { 1.0 / 2.0f64.sqrt() } else { 1.0 } * PLAYER_SPEED * elapsed;
    let dx = match (game.events.key_left, game.events.key_right) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved * 1.5,
      (false, true) => moved * 1.5,
    };

    let dy = match (game.events.key_up, game.events.key_down) {
      (true, true) | (false, false) => 0.0,
      (true, false) => -moved * 0.75,
      (false, true) => moved * 0.75,
    };

    self.player.rect.x += dx;
    self.player.rect.y += dy;

    let movable_region = Rectangle {
      x: 0.0,
      y: 0.0,
      w: game.output_size().0 * 2.0,
      h: game.output_size().1 * 2.0,
    };

    let curr_rect = game.renderer.viewport();
    let rect = viewport_move(&game, curr_rect, dx, dy);
    game.renderer.set_viewport(rect.to_sdl());

    self.background.render(&mut game.renderer);

    self.player.rect = self.player.rect.move_inside(movable_region).unwrap();
    self.player.current =
    if dx == 0.0 && dy < 0.0       { Orientation::Up }
    else if dx > 0.0 && dy < 0.0   { Orientation::UpRight }
    else if dx < 0.0 && dy < 0.0   { Orientation::UpLeft }
    else if dx == 0.0 && dy == 0.0 { self.player.heading }
    else if dx > 0.0 && dy == 0.0  { Orientation::Right }
    else if dx < 0.0 && dy == 0.0  { Orientation::Left }
    else if dx == 0.0 && dy > 0.0  { Orientation::Down }
    else if dx > 0.0 && dy > 0.0   { Orientation::DownRight }
    else if dx < 0.0 && dy > 0.0   { Orientation::DownLeft }
    else { unreachable!() };

    self.player.heading = self.player.current;
    for x in 0..TILES_PCS_W {
      for y in 0..TILES_PCS_H {
        let index = x * TILES_PCS_H + y;
        game.renderer.copy_sprite(&self.sprite_sheet[(self.tiles[index].current-1) as usize], self.tiles[index].rect);
      }
    }
    self.zombie.update(elapsed);
    self.zombie.render(&mut game.renderer);

    match game.events.mouse_click {
      Some(_) => {
        let index = 211 + self.player.current as usize * 5 + self.player.fire_anim_index as usize;
        game.renderer.copy_sprite(&self.player.sprites[index], self.player.rect);
        self.player.fire_anim_index =
          if self.player.fire_anim_index < 4u32 { self.player.fire_anim_index + 1u32 } else { 0u32 };
        if self.player.fire_anim_index == 0 {
          game.play_sound(&self.pistol);
        }
      },
      None => {
        let index = self.player.current as usize * 28 + self.player.move_anim_index as usize;
        game.renderer.copy_sprite(&self.player.sprites[index], self.player.rect);
        self.player.move_anim_index =
          if dx == 0.0 && dy == 0.0 { 0u32 } else if self.player.move_anim_index < 13u32 { self.player.move_anim_index + 1u32 } else { 0u32 };
      },
    };

    let scale = game.renderer.scale();
    if game.events.zoom_in == true && scale.0 <= 2.0 && scale.1 <= 2.0 {
      let _ = game.renderer.set_scale(scale.0 + ZOOM_SPEED, scale.1 + ZOOM_SPEED);
    } else if game.events.zoom_out == true && scale.0 > 0.85 && scale.1 > 0.85 {
      let _ = game.renderer.set_scale(scale.0 - ZOOM_SPEED, scale.1 - ZOOM_SPEED);
    }

    ViewAction::None
  }
}
<|endoftext|>


<commit_before>use ::find_interval;

pub struct Distribution1D {
    pub func: Vec<f32>,
    cdf: Vec<f32>,
    pub func_int: f32,
}

impl Distribution1D {
    pub fn new(f: &[f32]) -> Distribution1D {
        let n = f.len();
        assert!(n >= 2, "Need at least 2 values in a distribution");
        let func = Vec::from(f);
        let mut cdf = vec![0.0; n + 1];
        // compute integral of step function at xi
        cdf[0] = 0.0;
        for i in 1..n + 1 {
            cdf[i] = cdf[i - 1] + func[i - 1] / n as f32;
        }
        // transform step function integral into CDF
        let func_int = cdf[n];
        if func_int == 0.0 {
            for i in 0..n + 1 {
                cdf[i] = i as f32 / n as f32;
            }
        } else {
            for i in 0..n + 1 {
                cdf[i] /= func_int;
            }
        }

        Distribution1D {
            cdf: cdf,
            func: func,
            func_int: func_int,
        }
    }

    pub fn count(&self) -> usize {
        self.func.len()
    }

    pub fn sample_continuous(&self, u: f32) -> (f32, f32, usize) {
        // Find surrounding CDF segments and offset
        let offset = find_interval(self.cdf.len(), |i| self.cdf[i] <= u);
        // compute offset along CDF segment
        let mut du = u - self.cdf[offset];
        if self.cdf[offset + 1] - self.cdf[offset] > 0.0 {
            du /= self.cdf[offset + 1] - self.cdf[offset];
        }
        // compute PDF for sampled offset
        let pdf = self.func[offset] / self.func_int;

        // return x ∈ [0,1) corresponding to sample
        let x = (offset as f32 + du) / self.count() as f32;

        (x, pdf, offset)
    }

    pub fn sample_discrete(&self, u: f32) -> (usize, f32) {
        let offset = find_interval(self.cdf.len(), |i| self.cdf[i] <= u);
        let pdf = if self.func_int > 0.0 {
            self.func[offset] / (self.func_int * self.count() as f32)
        } else {
            0.0
        };

        (offset, pdf)
    }

    // TODO pdf_discrete
}

#[test]
fn test_discrete() {
    let func = [0.0, 1.0, 0.0, 3.0];
    let distrib = Distribution1D::new(&func[..]);

    assert_eq!(4, distrib.count());

    assert_eq!((1, 0.25), distrib.sample_discrete(0.0));
    assert_eq!((1, 0.25), distrib.sample_discrete(0.125));
    assert_eq!((1, 0.25), distrib.sample_discrete(0.24999));
    assert_eq!((3, 0.75), distrib.sample_discrete(0.250001));
    assert_eq!((3, 0.75), distrib.sample_discrete(0.625));
    assert_eq!((3, 0.75), distrib.sample_discrete(::ONE_MINUS_EPSILON));
    assert_eq!((3, 0.75), distrib.sample_discrete(1.0));
}
<commit_msg>Fix some off by one bugs in Distribution1D<commit_after>use ::find_interval;

pub struct Distribution1D {
    pub func: Vec<f32>,
    cdf: Vec<f32>,
    pub func_int: f32,
}

impl Distribution1D {
    pub fn new(f: &[f32]) -> Distribution1D {
        let n = f.len();
        let func = Vec::from(f);
        let mut cdf = vec![0.0; n + 1];
        // compute integral of step function at xi
        cdf[0] = 0.0;
        for i in 1..(n + 1) {
            cdf[i] = cdf[i - 1] + func[i - 1] / n as f32;
        }
        // transform step function integral into CDF
        let func_int = cdf[n];
        if func_int == 0.0 {
            for i in 1..(n + 1) {
                cdf[i] = i as f32 / n as f32;
            }
        } else {
            for i in 1..(n + 1) {
                cdf[i] /= func_int;
            }
        }

        Distribution1D {
            cdf: cdf,
            func: func,
            func_int: func_int,
        }
    }

    pub fn count(&self) -> usize {
        self.func.len()
    }

    pub fn sample_continuous(&self, u: f32) -> (f32, f32, usize) {
        // Find surrounding CDF segments and offset
        let offset = find_interval(self.cdf.len(), |i| self.cdf[i] <= u);
        // compute offset along CDF segment
        let mut du = u - self.cdf[offset];
        if self.cdf[offset + 1] - self.cdf[offset] > 0.0 {
            du /= self.cdf[offset + 1] - self.cdf[offset];
        }
        // compute PDF for sampled offset
        let pdf = self.func[offset] / self.func_int;

        // return x ∈ [0,1) corresponding to sample
        let x = (offset as f32 + du) / self.count() as f32;

        (x, pdf, offset)
    }

    pub fn sample_discrete(&self, u: f32) -> (usize, f32) {
        let offset = find_interval(self.cdf.len(), |i| self.cdf[i] <= u);
        let pdf = if self.func_int > 0.0 {
            self.func[offset] / (self.func_int * self.count() as f32)
        } else {
            0.0
        };

        (offset, pdf)
    }

    // TODO pdf_discrete
}

#[test]
fn test_discrete() {
    let func = [0.0, 1.0, 0.0, 3.0];
    let distrib = Distribution1D::new(&func[..]);

    assert_eq!(4, distrib.count());

    assert_eq!((1, 0.25), distrib.sample_discrete(0.0));
    assert_eq!((1, 0.25), distrib.sample_discrete(0.125));
    assert_eq!((1, 0.25), distrib.sample_discrete(0.24999));
    assert_eq!((3, 0.75), distrib.sample_discrete(0.250001));
    assert_eq!((3, 0.75), distrib.sample_discrete(0.625));
    assert_eq!((3, 0.75), distrib.sample_discrete(::ONE_MINUS_EPSILON));
    assert_eq!((3, 0.75), distrib.sample_discrete(1.0));
}
<|endoftext|>


<commit_before>#![feature(test)]
#![feature(core)]
#![feature(collections)]

extern crate differential_dataflow;
extern crate rand;
extern crate test;

use rand::{Rng, SeedableRng, StdRng, Rand};
use test::Bencher;
use std::ptr;
use std::mem;

use differential_dataflow::sort::*;

fn random_vec<T: Rand>(size: usize) -> Vec<T> {
    let seed: &[_] = &[1, 2, 3, 4];
    let mut rng: StdRng = SeedableRng::from_seed(seed);
    // println!("{}", rng.gen::<f64>());

    let mut result = Vec::with_capacity(size);
    for _ in 0..size {
        result.push(rng.gen());
    }

    result
}

// #[bench] fn sort_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
//     bencher.iter(|| data.clone().sort());
// }

#[bench] fn qsort1_bench(bencher: &mut Bencher) {
    let mut data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
    // for i in 0..data.len()  { data[i].0 = i as u64; }

    bencher.iter(|| qsort_by(&mut data.clone(), &|x|&x.0));
}
#[bench] fn qsort2_bench(bencher: &mut Bencher) {
    let mut data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
    // for i in 0..data.len()  { data[i].0 = i as u64; }

    bencher.iter(|| qsort2_by(&mut data.clone(), &|x|&x.0));
}
// //
//
// #[bench] fn rsort8_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
//     bencher.iter(|| rsort8(&mut data.clone(), &|&x| x.0));
// }

// #[bench] fn rsort88_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
//     bencher.iter(|| rsort88(&mut data.clone(), &|&x| x.0));
// }

// #[bench] fn rsort888_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
//     bencher.iter(|| rsort888(&mut data.clone(), &|&x| x.0));
// }

// #[bench] fn rsort8888_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
//     bencher.iter(|| rsort8888(&mut data.clone(), &|&x| x.0));
// }

#[bench] fn push_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 14);
    let mut buff = Vec::new();

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
    });
}

#[bench] fn clone_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 14);

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        let mut buff = data.clone();
    });
}

#[bench] fn shuffle_r_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
    let mut buff = Vec::new();
    let mut vecs = vec![Vec::new(); 16];

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
        let mut upper = [0; 16];
        rstep(&mut buff, &|&x| x.0, &mut upper);

        let mut cursor = 0;
        for i in 0..vecs.len() {
            vecs[i].clear();
            vecs[i].push_all(&buff[cursor as usize ..upper[i] as usize]);
            cursor = upper[i];
        }
    });
}

#[bench] fn shuffle_s_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
    let mut buff = Vec::new();
    let mut buff2 = data.clone();
    let mut vecs = vec![Vec::new(); 16];

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
        let mut upper = [0; 16];
        rstep_s(&mut buff, &mut buff2, &|&x| x.0, &mut upper);

        let mut cursor = 0;
        for i in 0..vecs.len() {
            vecs[i].clear();
            vecs[i].push_all(&buff2[cursor as usize ..upper[i] as usize]);
            cursor = upper[i];
        }
    });
}

#[bench] fn radix_r_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
    let mut buff = Vec::new();

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
        let mut ranges = [0; 16];
        rstep(&mut buff, &|&x| x.0, &mut ranges);
    });
}

#[bench] fn shuffle_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
    let mut buff = Vec::new();
    let mut vecs = vec![Vec::new(); 16];

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
        for i in 0..vecs.len() { vecs[i].clear(); }
        for (i, d) in buff.drain() {
            unsafe { vecs.get_unchecked_mut((i & 0x0F) as usize) }.push(d);
        }
    });
}

pub fn rsort8<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F) {
    rstep8(slice, func, |slice| slice.sort());
}

pub fn rsort88<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F) {
    rstep8(slice, func, |slice| rstep8(slice, &|x| func(x) >> 8, |x| x.sort()));
}

pub fn rsort888<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F) {
    rstep8(slice, func, |slice| rstep8(slice, &|x| func(x) >> 8, |slice| rstep8(slice, &|x| func(x) >> 16, |x| x.sort())));
}

pub fn rsort8888<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F) {
    rstep8(slice, func, |slice|
        rstep8(slice, &|x| func(x) >> 8, |slice|
            rstep8(slice, &|x| func(x) >> 16, |slice|
                rstep8(slice, &|x| func(x) >> 24, |x| x.sort()))));
}

pub fn rstep8<T:Ord+Copy, F: Fn(&T)->u64, A: Fn(&mut [T])>(slice: &mut [T], func: &F, andthen: A) {
    let mut ranges = [(0, 0); 256];
    for elem in slice.iter() { ranges[(func(elem) & 0xFF) as usize].1 += 1; }
    for i in 1..ranges.len() { ranges[i].0 = ranges[i-1].1;
                               ranges[i].1 += ranges[i].0; }

    for i in 0..256 {
        while ranges[i].0 < ranges[i].1 {
            let dst = (func(&slice[ranges[i].0]) & 0xFF) as usize;
            if dst != i {
                slice.swap(ranges[i].0, ranges[dst].0);
            }
            ranges[dst].0 += 1;
        }
    }

    let mut cursor = 0;
    for i in 0..256 {
        if cursor + 16 < ranges[i].0  { andthen(&mut slice[cursor..ranges[i].0]); }
        else                          { isort(&mut slice[cursor..ranges[i].0]) }
        cursor = ranges[i].0;
    }
}

pub fn rstep<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F, upper: &mut [u32; 16]) {
    let mut lower = [0; 16];
    unsafe {
        for elem in slice.iter() { *upper.get_unchecked_mut((func(elem) & 0x0F) as usize) += 1; }
        for i in 1..upper.len() { lower[i] = upper[i-1]; upper[i] += lower[i]; }

        for i in 0..16 {
            while *lower.get_unchecked_mut(i) < *upper.get_unchecked_mut(i) {
                let dst = (func(&slice.get_unchecked_mut(*lower.get_unchecked_mut(i) as usize)) & 0x0F) as usize;
                if dst != i {
                    // slice.swap(lower[i] as usize, lower[dst] as usize);
                    let pa: *mut T = slice.get_unchecked_mut(*lower.get_unchecked_mut(i) as usize);
                    let pb: *mut T = slice.get_unchecked_mut(*lower.get_unchecked_mut(dst) as usize);
                    ptr::swap(pa, pb);
                }
                *lower.get_unchecked_mut(dst) += 1;
            }
        }
    }
}

pub fn rstep_s<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], other: &mut [T], func: &F, upper: &mut [u32; 16]) {
    let mut lower = [0; 16];
    unsafe {
        for elem in slice.iter() { *upper.get_unchecked_mut((func(elem) & 0x0F) as usize) += 1; }
        for i in 1..upper.len() { lower[i] = upper[i-1]; upper[i] += lower[i]; }

        for x in slice {
            let dst = (func(x) & 0x0F) as usize;
            *other.get_unchecked_mut(lower[dst] as usize) = *x;
            lower[dst] += 1;
        }
    }
}
<commit_msg>renamed benches to co-located like results<commit_after>#![feature(test)]
#![feature(core)]
#![feature(collections)]
#![feature(collections_drain)]

extern crate differential_dataflow;
extern crate rand;
extern crate test;

use rand::{Rng, SeedableRng, StdRng, Rand};
use test::Bencher;
use std::ptr;
use std::mem;

use differential_dataflow::sort::*;

fn random_vec<T: Rand>(size: usize) -> Vec<T> {
    let seed: &[_] = &[1, 2, 3, 4];
    let mut rng: StdRng = SeedableRng::from_seed(seed);
    // println!("{}", rng.gen::<f64>());

    let mut result = Vec::with_capacity(size);
    for _ in 0..size {
        result.push(rng.gen());
    }

    result
}

#[bench] fn sort_sort(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    bencher.iter(|| data.clone().sort());
}

#[bench] fn sort_qsort1(bencher: &mut Bencher) {
    let mut data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    // for i in 0..data.len()  { data[i].0 = i as u64; }

    bencher.iter(|| qsort_by(&mut data.clone(), &|x|&x.0));
}
#[bench] fn sort_qsort2(bencher: &mut Bencher) {
    let mut data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    // for i in 0..data.len()  { data[i].0 = i as u64; }

    bencher.iter(|| qsort2_by(&mut data.clone(), &|x|&x.0));
}
// //
//
// #[bench] fn rsort8_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
//     bencher.iter(|| rsort8(&mut data.clone(), &|&x| x.0));
// }

// #[bench] fn rsort88_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 16);
//     bencher.iter(|| rsort88(&mut data.clone(), &|&x| x.0));
// }

#[bench] fn sort_rsort888(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    bencher.iter(|| rsort888(&mut data.clone(), &|&x| x.0));
}

#[bench] fn sort_rsort8888(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    bencher.iter(|| rsort8888(&mut data.clone(), &|&x| x.0));
}

// #[bench] fn push_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 14);
//     let mut buff = Vec::new();
//
//     bencher.bytes = 5 * 8 * data.len() as u64;
//     bencher.iter(|| {
//         buff.clear();
//         buff.push_all(&data);
//     });
// }
//
// #[bench] fn clone_bench(bencher: &mut Bencher) {
//     let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 14);
//
//     bencher.bytes = 5 * 8 * data.len() as u64;
//     bencher.iter(|| {
//         let mut buff = data.clone();
//     });
// }

#[bench] fn endian_noop(bencher: &mut Bencher) {
    bencher.iter(|| { (0..1024u64).sum::<u64>() })
}

#[bench] fn endian_to_le(bencher: &mut Bencher) {
    bencher.iter(|| { (0..1024u64).map(|x| x.to_le()).sum::<u64>() })
}

#[bench] fn endian_to_be(bencher: &mut Bencher) {
    bencher.iter(|| { (0..1024u64).map(|x| x.to_be()).sum::<u64>() })
}

#[bench] fn shuffle_r_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    let mut buff = Vec::new();
    let mut vecs = vec![Vec::new(); 16];

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
        let mut upper = [0; 16];
        rstep(&mut buff, &|&x| x.0, &mut upper);

        let mut cursor = 0;
        for i in 0..vecs.len() {
            vecs[i].clear();
            vecs[i].push_all(&buff[cursor as usize ..upper[i] as usize]);
            cursor = upper[i];
        }
    });
}

#[bench] fn shuffle_s_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    let mut buff = Vec::new();
    let mut buff2 = data.clone();
    let mut vecs = vec![Vec::new(); 16];

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
        let mut upper = [0; 16];
        rstep_s(&mut buff, &mut buff2, &|&x| x.0, &mut upper);

        let mut cursor = 0;
        for i in 0..vecs.len() {
            vecs[i].clear();
            vecs[i].push_all(&buff2[cursor as usize ..upper[i] as usize]);
            cursor = upper[i];
        }
    });
}

#[bench] fn radix_r_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    let mut buff = Vec::new();

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
        let mut ranges = [0; 16];
        rstep(&mut buff, &|&x| x.0, &mut ranges);
    });
}

#[bench] fn shuffle_bench(bencher: &mut Bencher) {
    let data = random_vec::<(u64, (u64, u64, u64, u64))>(1 << 12);
    let mut buff = Vec::new();
    let mut vecs = vec![Vec::new(); 16];

    bencher.bytes = 5 * 8 * data.len() as u64;
    bencher.iter(|| {
        buff.clear();
        buff.push_all(&data);
        for i in 0..vecs.len() { vecs[i].clear(); }
        for (i, d) in buff.drain(..) {
            unsafe { vecs.get_unchecked_mut((i & 0x0F) as usize) }.push(d);
        }
    });
}

pub fn rsort8<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F) {
    rstep8(slice, func, |slice| slice.sort());
}

pub fn rsort88<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F) {
    rstep8(slice, func, |slice| rstep8(slice, &|x| func(x) >> 8, |x| x.sort()));
}

pub fn rsort888<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F) {
    rstep8(slice, func, |slice|
        rstep8(slice, &|x| func(x) >> 8, |slice|
            rstep8(slice, &|x| func(x) >> 16, |x| qsort(x))));
}

pub fn rsort8888<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F) {
    rstep8(slice, func, |slice|
        rstep8(slice, &|x| func(x) >> 8, |slice|
            rstep8(slice, &|x| func(x) >> 16, |slice|
                rstep8(slice, &|x| func(x) >> 24, |x| qsort(x)))));
}

pub fn rstep8<T:Ord+Copy, F: Fn(&T)->u64, A: Fn(&mut [T])>(slice: &mut [T], func: &F, andthen: A) {
    let mut ranges = [(0, 0); 256];
    // for elem in slice.iter() { ranges[(func(elem) & 0xFF) as usize].1 += 1; }
    for elem in slice.iter() { unsafe { ranges.get_unchecked_mut((func(elem) & 0xFF) as usize).1 += 1; } }
    for i in 1..ranges.len() { ranges[i].0 = ranges[i-1].1;
                               ranges[i].1 += ranges[i].0; }

    for i in 0..256 {
        while ranges[i].0 < ranges[i].1 {
            let dst = (func(&slice[ranges[i].0]) & 0xFF) as usize;
            if dst != i {
                slice.swap(ranges[i].0, ranges[dst].0);
            }
            ranges[dst].0 += 1;
        }
    }

    let mut cursor = 0;
    for i in 0..256 {
        if cursor + 16 < ranges[i].0  { andthen(&mut slice[cursor..ranges[i].0]); }
        else                          { isort(&mut slice[cursor..ranges[i].0]) }
        cursor = ranges[i].0;
    }
}

pub fn rstep<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], func: &F, upper: &mut [u32; 16]) {
    let mut lower = [0; 16];
    unsafe {
        for elem in slice.iter() { *upper.get_unchecked_mut((func(elem) & 0x0F) as usize) += 1; }
        for i in 1..upper.len() { lower[i] = upper[i-1]; upper[i] += lower[i]; }

        for i in 0..16 {
            while *lower.get_unchecked_mut(i) < *upper.get_unchecked_mut(i) {
                let dst = (func(&slice.get_unchecked_mut(*lower.get_unchecked_mut(i) as usize)) & 0x0F) as usize;
                if dst != i {
                    // slice.swap(lower[i] as usize, lower[dst] as usize);
                    let pa: *mut T = slice.get_unchecked_mut(*lower.get_unchecked_mut(i) as usize);
                    let pb: *mut T = slice.get_unchecked_mut(*lower.get_unchecked_mut(dst) as usize);
                    ptr::swap(pa, pb);
                }
                *lower.get_unchecked_mut(dst) += 1;
            }
        }
    }
}

pub fn rstep_s<T:Ord+Copy, F: Fn(&T)->u64>(slice: &mut [T], other: &mut [T], func: &F, upper: &mut [u32; 16]) {
    let mut lower = [0; 16];
    unsafe {
        for elem in slice.iter() { *upper.get_unchecked_mut((func(elem) & 0x0F) as usize) += 1; }
        for i in 1..upper.len() { lower[i] = upper[i-1]; upper[i] += lower[i]; }

        for x in slice {
            let dst = (func(x) & 0x0F) as usize;
            *other.get_unchecked_mut(lower[dst] as usize) = *x;
            lower[dst] += 1;
        }
    }
}
<|endoftext|>


<commit_before>//! Binding shim for libz80e-core
extern crate libc;
use self::libc::{ c_int, c_void, pthread_mutex_t };

#[allow(dead_code)]
#[repr(C)]
pub struct z80e_registers {
    af: u16,
    bc: u16,
    de: u16,
    hl: u16,
    _af: u16,
    _bc: u16,
    _de: u16,
    _hl: u16,
    pc: u16,
    sp: u16,
    ix: u16,
    iy: u16,
    i: u8,
    r: u8,
    wz: u16,
}

#[allow(dead_code)]
#[repr(C)]
pub struct z80e_io_device {
    pub device: *mut c_void,
    pub read_in: extern fn(*mut c_void) -> u8,
    pub write_out: extern fn(*mut c_void, u8),
}

#[allow(dead_code)]
#[repr(C)]
pub struct z80e_cpu {
    pub devices: [z80e_io_device; 0x100],
    registers: z80e_registers,
    iff1: bool,
    iff2: bool,
    int_mode: u8,
    iff_wait: bool,
    halted: bool,
    prefix: u16,
    pub memory: *mut c_void,
    pub read_byte: extern fn(*mut c_void, u16) -> u8,
    pub write_byte: extern fn(*mut c_void, u16, u8),
    bus_lock: pthread_mutex_t,
    interrupt: bool,
    bus: u8,
}

#[allow(dead_code)]
#[link(name = "z80e-core")]
extern {
    pub fn cpu_init() -> *mut z80e_cpu;
    pub fn cpu_free(cpu: *mut z80e_cpu);
    pub fn cpu_interrupt(cpu: *mut z80e_cpu, bus: u8) -> c_int;
    pub fn cpu_try_interrupt(cpu: *mut z80e_cpu, bus: u8) -> c_int;
//    pub fn cpu_clear_interrupt(cpu: *mut z80e_cpu) -> c_int;
//    pub fn cpu_try_clear_interrupt(cpu: *mut z80e_cpu) -> c_int;
    pub fn cpu_execute(cpu: *mut z80e_cpu, cycles: c_int) -> c_int;
}
<commit_msg>Add z80e_core::cpu_{try_}clear_interrupt()<commit_after>//! Binding shim for libz80e-core
extern crate libc;
use self::libc::{ c_int, c_void, pthread_mutex_t };

#[allow(dead_code)]
#[repr(C)]
pub struct z80e_registers {
    af: u16,
    bc: u16,
    de: u16,
    hl: u16,
    _af: u16,
    _bc: u16,
    _de: u16,
    _hl: u16,
    pc: u16,
    sp: u16,
    ix: u16,
    iy: u16,
    i: u8,
    r: u8,
    wz: u16,
}

#[allow(dead_code)]
#[repr(C)]
pub struct z80e_io_device {
    pub device: *mut c_void,
    pub read_in: extern fn(*mut c_void) -> u8,
    pub write_out: extern fn(*mut c_void, u8),
}

#[allow(dead_code)]
#[repr(C)]
pub struct z80e_cpu {
    pub devices: [z80e_io_device; 0x100],
    registers: z80e_registers,
    iff1: bool,
    iff2: bool,
    int_mode: u8,
    iff_wait: bool,
    halted: bool,
    prefix: u16,
    pub memory: *mut c_void,
    pub read_byte: extern fn(*mut c_void, u16) -> u8,
    pub write_byte: extern fn(*mut c_void, u16, u8),
    bus_lock: pthread_mutex_t,
    interrupt: bool,
    bus: u8,
}

#[allow(dead_code)]
#[link(name = "z80e-core")]
extern {
    pub fn cpu_init() -> *mut z80e_cpu;
    pub fn cpu_free(cpu: *mut z80e_cpu);
    pub fn cpu_interrupt(cpu: *mut z80e_cpu, bus: u8) -> c_int;
    pub fn cpu_clear_interrupt(cpu: *mut z80e_cpu) -> c_int;
    pub fn cpu_try_interrupt(cpu: *mut z80e_cpu, bus: u8) -> c_int;
    pub fn cpu_try_clear_interrupt(cpu: *mut z80e_cpu) -> c_int;
    pub fn cpu_execute(cpu: *mut z80e_cpu, cycles: c_int) -> c_int;
}
<|endoftext|>


<commit_before>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

/// Creates C-like enums which preserve unknown (un-enumerated) values.
///
/// If you wanted an enum like:
/// ```
/// #[repr(u32)]
/// pub enum HardenedBool {
///     True = 0x739,
///     False = 0x146,
///     Unknown(u32),
/// }
/// ```
///
/// Where the `Unknown` discriminator would be the catch-all for any
/// non-enumerated values, you can use `with_unknown!` as follows:
///
/// ```
/// with_unknown! {
///     pub enum HardenedBool: u32 {
///         True = 0x739,
///         False = 0x14d,
///     }
/// }
/// ```
///
/// This "enum" can be used later in match statements:
/// ```
/// match foo {
///     HardenedBool::True => do_the_thing(),
///     HardenedBool::False => do_the_opposite_thing(),
///     HardenedBool(x) => panic!("Oh noes! {} is neither True nor False!", x),
/// }
/// ```
///
/// Behind the scenes, `with_unknown!` implements a newtype struct and
/// creates associated constants for each of the enumerated values.
/// The struct also implements `Copy`, `Clone`, `PartialEq`, `Eq`,
/// `PartialOrd`, `Ord`, `Hash`, `Debug` and `Display` (including the hex,
/// octal and binary versions).
///
/// In addition, `serde::Serialize` and `serde::Deserialize` are
/// implemented.  The serialized form is a string for known values and an
/// integer for all unknown values.
#[macro_export]
macro_rules! with_unknown {
    (
        $(
            $(#[$outer:meta])*
            $vis:vis enum $Enum:ident: $type:ty {
                $(
                    $(#[$inner:meta])*
                    $enumerator:ident = $value:expr,
                )*
            }
        )*
    ) => {$(
        $(#[$outer])*
        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
        #[repr(transparent)]
        $vis struct $Enum(pub $type);

        #[allow(non_upper_case_globals)]
        impl $Enum {
            $(
                $(#[$inner])*
                $vis const $enumerator: $Enum = $Enum($value);
            )*
        }

        impl From<$Enum> for $type {
            fn from(v: $Enum) -> $type {
                v.0
            }
        }

        // Implement the various display traits.
        $crate::__impl_fmt_unknown!(Display, "{}", "{}", $Enum { $($enumerator),* });
        $crate::__impl_fmt_unknown!(LowerHex, "{:x}", "{:#x}", $Enum { $($enumerator),* });
        $crate::__impl_fmt_unknown!(UpperHex, "{:X}", "{:#X}", $Enum { $($enumerator),* });
        $crate::__impl_fmt_unknown!(Octal, "{:o}", "{:#o}", $Enum { $($enumerator),* });
        $crate::__impl_fmt_unknown!(Binary, "{:b}", "{:#b}", $Enum { $($enumerator),* });

        // Manually implement Serialize and Deserialize to have tight control over how
        // the struct is serialized.
        const _: () = {
            use serde::ser::{Serialize, Serializer};
            use serde::de::{Deserialize, Deserializer, Error, Visitor};
            use std::convert::TryFrom;

            impl Serialize for $Enum {
                /// Serializes the enumerated values.  All named discriminants are
                /// serialized to strings.  All unknown values are serialized as
                /// integers.
                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where
                    S: Serializer,
                {
                    match *self {
                        $(
                            $Enum::$enumerator => serializer.serialize_str(stringify!($enumerator)),
                        )*
                        $Enum(value) => value.serialize(serializer),
                    }
                }
            }

            // The `EnumVistor` assists in deserializing the value.
            struct EnumVisitor;
            impl<'de> Visitor<'de> for EnumVisitor {
                type Value = $Enum;

                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                    f.write_str(concat!("A valid enumerator of ", stringify!($Enum)))
                }

                fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
                    match value {
                        $(
                            stringify!($enumerator) => Ok($Enum::$enumerator),
                        )*
                        _ => Err(E::custom(format!("unrecognized: {}", value))),
                    }
                }
                $crate::__expand_visit_fn!(visit_i8, i8, $Enum, $type);
                $crate::__expand_visit_fn!(visit_i16, i16, $Enum, $type);
                $crate::__expand_visit_fn!(visit_i32, i32, $Enum, $type);
                $crate::__expand_visit_fn!(visit_i64, i64, $Enum, $type);
                $crate::__expand_visit_fn!(visit_u8, u8, $Enum, $type);
                $crate::__expand_visit_fn!(visit_u16, u16, $Enum, $type);
                $crate::__expand_visit_fn!(visit_u32, u32, $Enum, $type);
                $crate::__expand_visit_fn!(visit_u64, u64, $Enum, $type);
            }

            impl<'de> Deserialize<'de> for $Enum {
                /// Deserializes the value by forwarding to `deserialize_any`.
                /// `deserialize_any` will forward strings to the string visitor
                /// and forward integers to the appropriate integer visitor.
                fn deserialize<D>(deserializer: D) -> Result<$Enum, D::Error>
                where
                    D: Deserializer<'de>,
                {
                    deserializer.deserialize_any(EnumVisitor)
                }
            }
        };
    )*};
}

#[macro_export]
macro_rules! __expand_visit_fn {
    ($visit_func:ident, $ser_type:ty, $Enum:ident, $enum_type:ty) => {
        fn $visit_func<E>(self, value: $ser_type) -> Result<Self::Value, E>
        where
            E: Error,
        {
            match <$enum_type>::try_from(value) {
                Ok(v) => Ok($Enum(v)),
                Err(_) => Err(E::custom(format!(
                    "cannot convert {:?} to {}({})",
                    value,
                    stringify!($Enum),
                    stringify!($enum_type)
                ))),
            }
        }
    };
}

// Helper macro for implementing the various formatting traits.
#[macro_export]
macro_rules! __impl_fmt_unknown {
    (
        $Trait:ident, $Fmt:literal, $Alt:literal, $Enum:ident {
            $($enumerator:ident),*
        }
    ) => {
        impl std::fmt::$Trait for $Enum {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                match *self {
                    $(
                        $Enum::$enumerator => write!(f, "{}", stringify!($enumerator)),
                    )*
                    $Enum(value) => {
                        if f.alternate() {
                            write!(f, concat!(stringify!($Enum), "(", $Alt, ")"), value)
                        } else {
                            write!(f, concat!(stringify!($Enum), "(", $Fmt, ")"), value)
                        }
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use anyhow::Result;
    use serde::{Deserialize, Serialize};

    with_unknown! {
        pub enum HardenedBool: u32 {
            True = 0x739,
            False = 0x14d,
        }

        // Check the top-level repeat in the macro.
        enum Misc: u8 {
            X = 0,
            Y = 1,
            Z = 2,
        }
    }

    #[test]
    fn test_display() -> Result<()> {
        let t = HardenedBool::True;
        assert_eq!(t.to_string(), "True");

        let f = HardenedBool::False;
        assert_eq!(f.to_string(), "False");

        let j = HardenedBool(0x6A);
        assert_eq!(j.to_string(), "HardenedBool(106)");
        assert_eq!(format!("{:x}", j), "HardenedBool(6a)");
        assert_eq!(format!("{:#x}", j), "HardenedBool(0x6a)");
        assert_eq!(format!("{:X}", j), "HardenedBool(6A)");
        assert_eq!(format!("{:o}", j), "HardenedBool(152)");
        assert_eq!(format!("{:b}", j), "HardenedBool(1101010)");
        assert_eq!(format!("{:#b}", j), "HardenedBool(0b1101010)");
        Ok(())
    }

    #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
    struct SomeBools {
        a: HardenedBool,
        b: HardenedBool,
        c: HardenedBool,
    }

    #[test]
    fn test_conversion() -> Result<()> {
        let t = HardenedBool::True;
        let x = HardenedBool(12345);
        assert_eq!(u32::from(t), 0x739);
        assert_eq!(u32::from(x), 12345);
        Ok(())
    }

    #[test]
    fn test_serde() -> Result<()> {
        let b = SomeBools {
            a: HardenedBool::True,
            b: HardenedBool::False,
            c: HardenedBool(0x6a),
        };
        let json = serde_json::to_string(&b)?;
        assert_eq!(json, r#"{"a":"True","b":"False","c":106}"#);

        let de = serde_json::from_str::<SomeBools>(&json)?;
        assert_eq!(de, b);
        Ok(())
    }

    #[test]
    fn test_serde_error() -> Result<()> {
        let json = r#"{"a":"True","b":"False","c":-1}"#;
        let de = serde_json::from_str::<SomeBools>(json);
        let err = de.unwrap_err().to_string();
        assert_eq!(
            err,
            "cannot convert -1 to HardenedBool(u32) at line 1 column 30"
        );
        Ok(())
    }
}
<commit_msg>[otlib] Enhance `with_unknown!`<commit_after>// Copyright lowRISC contributors.
// Licensed under the Apache License, Version 2.0, see LICENSE for details.
// SPDX-License-Identifier: Apache-2.0

/// Creates C-like enums which preserve unknown (un-enumerated) values.
///
/// If you wanted an enum like:
/// ```
/// #[repr(u32)]
/// pub enum HardenedBool {
///     True = 0x739,
///     False = 0x146,
///     Unknown(u32),
/// }
/// ```
///
/// Where the `Unknown` discriminator would be the catch-all for any
/// non-enumerated values, you can use `with_unknown!` as follows:
///
/// ```
/// with_unknown! {
///     pub enum HardenedBool: u32 {
///         True = 0x739,
///         False = 0x14d,
///     }
/// }
/// ```
///
/// This "enum" can be used later in match statements:
/// ```
/// match foo {
///     HardenedBool::True => do_the_thing(),
///     HardenedBool::False => do_the_opposite_thing(),
///     HardenedBool(x) => panic!("Oh noes! {} is neither True nor False!", x),
/// }
/// ```
///
/// Behind the scenes, `with_unknown!` implements a newtype struct and
/// creates associated constants for each of the enumerated values.
/// The struct also implements `Copy`, `Clone`, `PartialEq`, `Eq`,
/// `PartialOrd`, `Ord`, `Hash`, `Debug` and `Display` (including the hex,
/// octal and binary versions).
///
/// In addition, `serde::Serialize` and `serde::Deserialize` are
/// implemented.  The serialized form is a string for known values and an
/// integer for all unknown values.
#[macro_export]
macro_rules! with_unknown {
    (
        $(
            $(#[$outer:meta])*
            $vis:vis enum $Enum:ident: $type:ty {
                $(
                    $(#[$inner:meta])*
                    $enumerator:ident = $value:expr,
                )*
            }
        )*
    ) => {$(
        $(#[$outer])*
        #[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord, Hash, Debug)]
        #[repr(transparent)]
        $vis struct $Enum(pub $type);

        #[allow(non_upper_case_globals)]
        impl $Enum {
            $(
                $(#[$inner])*
                $vis const $enumerator: $Enum = $Enum($value);
            )*
        }

        #[allow(dead_code)]
        impl $Enum {
            pub fn is_known_value(&self) -> bool {
                match *self {
                    $(
                        $Enum::$enumerator => true,
                    )*
                    _ => false,
                }
            }
        }

        impl From<$Enum> for $type {
            fn from(v: $Enum) -> $type {
                v.0
            }
        }

        $crate::__impl_try_from!(i8, $Enum);
        $crate::__impl_try_from!(i16, $Enum);
        $crate::__impl_try_from!(i32, $Enum);
        $crate::__impl_try_from!(i64, $Enum);
        $crate::__impl_try_from!(u8, $Enum);
        $crate::__impl_try_from!(u16, $Enum);
        $crate::__impl_try_from!(u32, $Enum);
        $crate::__impl_try_from!(u64, $Enum);

        // Implement the various display traits.
        $crate::__impl_fmt_unknown!(Display, "{}", "{}", $Enum { $($enumerator),* });
        $crate::__impl_fmt_unknown!(LowerHex, "{:x}", "{:#x}", $Enum { $($enumerator),* });
        $crate::__impl_fmt_unknown!(UpperHex, "{:X}", "{:#X}", $Enum { $($enumerator),* });
        $crate::__impl_fmt_unknown!(Octal, "{:o}", "{:#o}", $Enum { $($enumerator),* });
        $crate::__impl_fmt_unknown!(Binary, "{:b}", "{:#b}", $Enum { $($enumerator),* });

        // Manually implement Serialize and Deserialize to have tight control over how
        // the struct is serialized.
        const _: () = {
            use serde::ser::{Serialize, Serializer};
            use serde::de::{Deserialize, Deserializer, Error, Visitor};
            use std::convert::TryFrom;

            impl Serialize for $Enum {
                /// Serializes the enumerated values.  All named discriminants are
                /// serialized to strings.  All unknown values are serialized as
                /// integers.
                fn serialize<S>(&self, serializer: S) -> Result<S::Ok, S::Error>
                where
                    S: Serializer,
                {
                    match *self {
                        $(
                            $Enum::$enumerator => serializer.serialize_str(stringify!($enumerator)),
                        )*
                        $Enum(value) => value.serialize(serializer),
                    }
                }
            }

            // The `EnumVistor` assists in deserializing the value.
            struct EnumVisitor;
            impl<'de> Visitor<'de> for EnumVisitor {
                type Value = $Enum;

                fn expecting(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                    f.write_str(concat!("A valid enumerator of ", stringify!($Enum)))
                }

                fn visit_str<E: Error>(self, value: &str) -> Result<Self::Value, E> {
                    match value {
                        $(
                            stringify!($enumerator) => Ok($Enum::$enumerator),
                        )*
                        _ => Err(E::custom(format!("unrecognized: {}", value))),
                    }
                }
                $crate::__expand_visit_fn!(visit_i8, i8, $Enum, $type);
                $crate::__expand_visit_fn!(visit_i16, i16, $Enum, $type);
                $crate::__expand_visit_fn!(visit_i32, i32, $Enum, $type);
                $crate::__expand_visit_fn!(visit_i64, i64, $Enum, $type);
                $crate::__expand_visit_fn!(visit_u8, u8, $Enum, $type);
                $crate::__expand_visit_fn!(visit_u16, u16, $Enum, $type);
                $crate::__expand_visit_fn!(visit_u32, u32, $Enum, $type);
                $crate::__expand_visit_fn!(visit_u64, u64, $Enum, $type);
            }

            impl<'de> Deserialize<'de> for $Enum {
                /// Deserializes the value by forwarding to `deserialize_any`.
                /// `deserialize_any` will forward strings to the string visitor
                /// and forward integers to the appropriate integer visitor.
                fn deserialize<D>(deserializer: D) -> Result<$Enum, D::Error>
                where
                    D: Deserializer<'de>,
                {
                    deserializer.deserialize_any(EnumVisitor)
                }
            }
        };
    )*};
}

#[macro_export]
macro_rules! __impl_try_from {
    ($from_type:ty, $Enum:ident) => {
        impl TryFrom<$from_type> for $Enum {
            type Error = std::num::TryFromIntError;
            fn try_from(value: $from_type) -> Result<Self, Self::Error> {
                Ok($Enum(value.try_into()?))
            }
        }
    };
}

#[macro_export]
macro_rules! __expand_visit_fn {
    ($visit_func:ident, $ser_type:ty, $Enum:ident, $enum_type:ty) => {
        fn $visit_func<E>(self, value: $ser_type) -> Result<Self::Value, E>
        where
            E: Error,
        {
            match <$enum_type>::try_from(value) {
                Ok(v) => Ok($Enum(v)),
                Err(_) => Err(E::custom(format!(
                    "cannot convert {:?} to {}({})",
                    value,
                    stringify!($Enum),
                    stringify!($enum_type)
                ))),
            }
        }
    };
}

// Helper macro for implementing the various formatting traits.
#[macro_export]
macro_rules! __impl_fmt_unknown {
    (
        $Trait:ident, $Fmt:literal, $Alt:literal, $Enum:ident {
            $($enumerator:ident),*
        }
    ) => {
        impl std::fmt::$Trait for $Enum {
            fn fmt(&self, f: &mut std::fmt::Formatter) -> std::fmt::Result {
                match *self {
                    $(
                        $Enum::$enumerator => write!(f, "{}", stringify!($enumerator)),
                    )*
                    $Enum(value) => {
                        if f.alternate() {
                            write!(f, concat!(stringify!($Enum), "(", $Alt, ")"), value)
                        } else {
                            write!(f, concat!(stringify!($Enum), "(", $Fmt, ")"), value)
                        }
                    }
                }
            }
        }
    }
}

#[cfg(test)]
mod tests {
    use anyhow::Result;
    use serde::{Deserialize, Serialize};

    with_unknown! {
        enum HardenedBool: u32 {
            True = 0x739,
            False = 0x14d,
        }

        // Check the top-level repeat in the macro.
        enum Misc: u8 {
            X = 0,
            Y = 1,
            Z = 2,
        }
    }

    #[test]
    fn test_display() -> Result<()> {
        let t = HardenedBool::True;
        assert_eq!(t.to_string(), "True");
        assert!(t.is_known_value());

        let f = HardenedBool::False;
        assert_eq!(f.to_string(), "False");
        assert!(f.is_known_value());

        let j = HardenedBool(0x6A);
        assert!(!j.is_known_value());
        assert_eq!(j.to_string(), "HardenedBool(106)");
        assert_eq!(format!("{:x}", j), "HardenedBool(6a)");
        assert_eq!(format!("{:#x}", j), "HardenedBool(0x6a)");
        assert_eq!(format!("{:X}", j), "HardenedBool(6A)");
        assert_eq!(format!("{:o}", j), "HardenedBool(152)");
        assert_eq!(format!("{:b}", j), "HardenedBool(1101010)");
        assert_eq!(format!("{:#b}", j), "HardenedBool(0b1101010)");
        Ok(())
    }

    #[derive(Debug, Serialize, Deserialize, PartialEq, Eq)]
    struct SomeBools {
        a: HardenedBool,
        b: HardenedBool,
        c: HardenedBool,
    }

    #[test]
    fn test_conversion() -> Result<()> {
        let t = HardenedBool::True;
        let x = HardenedBool(12345);
        assert_eq!(u32::from(t), 0x739);
        assert_eq!(u32::from(x), 12345);
        Ok(())
    }

    #[test]
    fn test_serde() -> Result<()> {
        let b = SomeBools {
            a: HardenedBool::True,
            b: HardenedBool::False,
            c: HardenedBool(0x6a),
        };
        let json = serde_json::to_string(&b)?;
        assert_eq!(json, r#"{"a":"True","b":"False","c":106}"#);

        let de = serde_json::from_str::<SomeBools>(&json)?;
        assert_eq!(de, b);
        Ok(())
    }

    #[test]
    fn test_serde_error() -> Result<()> {
        let json = r#"{"a":"True","b":"False","c":-1}"#;
        let de = serde_json::from_str::<SomeBools>(json);
        let err = de.unwrap_err().to_string();
        assert_eq!(
            err,
            "cannot convert -1 to HardenedBool(u32) at line 1 column 30"
        );
        Ok(())
    }
}
<|endoftext|>