<commit_before>#[derive(Debug, PartialEq)]
pub enum Hashline {
    OpenEnv(Environment),
    PlainLine(String),
}

#[derive(Debug, PartialEq)]
pub struct Environment {
    indent_depth: usize,
    name: String,
    opts: String,
    comment: String,
    is_list_like: bool,
}

impl Environment {
    pub fn latex_begin(&self) -> String {
        format!(
            r"{dummy:ind$}\begin{{{name}}}{opts}{comment_sep}{comment}",
            name = self.name,
            opts = self.opts,
            comment = self.comment,
            dummy = "",
            ind = self.indent_depth,
            comment_sep = if self.comment.is_empty() { "" } else { " " }
        )
    }

    pub fn latex_end(&self) -> String {
        format!(
            r"{dummy:ind$}\end{{{name}}}",
            name = self.name,
            dummy = "",
            ind = self.indent_depth
        )
    }

    pub fn indent_depth(&self) -> usize {
        self.indent_depth
    }

    pub fn is_list_like(&self) -> bool {
        self.is_list_like
    }
}

#[inline]
fn list_env_parser(input: &str) -> nom::IResult<&str, ()> {
    use nom::branch::alt;
    use nom::bytes::complete::{is_a, tag};
    use nom::combinator::opt;

    let (input, _) = opt(is_a(" "))(input)?;
    let (input, _) = alt((tag("itemize"), tag("enumerate"), tag("description")))(input)?;
    Ok((input, ()))
}

#[inline]
fn escaped_colon(input: &str) -> nom::IResult<&str, &str> {
    use nom::bytes::complete::tag;
    use nom::sequence::preceded;

    preceded(tag("\\"), tag(":"))(input)
}

#[inline]
fn name_parser(input: &str) -> nom::IResult<&str, &str> {
    use nom::branch::alt;
    use nom::bytes::complete::{is_not, tag};

    alt((escaped_colon, tag("\\"), is_not("\\:%([{ \t")))(input)
}

#[inline]
fn opts_parser(input: &str) -> nom::IResult<&str, &str> {
    use nom::branch::alt;
    use nom::bytes::complete::{is_not, tag};

    alt((escaped_colon, tag("\\%"), tag("\\"), is_not("\\:%")))(input)
}

#[inline]
fn args_parser(input: &str) -> nom::IResult<&str, &str> {
    use nom::branch::alt;
    use nom::bytes::complete::{is_not, tag};

    alt((tag("\\%"), tag("\\"), is_not("\\%")))(input)
}

fn hashline_parser(input: &str) -> nom::IResult<&str, Hashline> {
    use nom::bytes::complete::{is_a, tag};
    use nom::combinator::{opt, rest};
    use nom::multi::{fold_many0, fold_many1};

    let (input, res) = opt(is_a(" "))(input)?;
    let ws = res.unwrap_or("");
    let (input, _) = tag("# ")(input)?;
    let (input, name) = fold_many1(
        name_parser,
        String::with_capacity(input.len()),
        |mut acc: String, item| {
            acc.push_str(item);
            acc
        },
    )(input)?;
    let (input, opts) = fold_many0(
        opts_parser,
        String::with_capacity(input.len()),
        |mut acc: String, item| {
            acc.push_str(item);
            acc
        },
    )(input)?;
    let (input, _) = tag(":")(input)?;
    let (input, args) = fold_many0(
        args_parser,
        String::with_capacity(input.len()),
        |mut acc: String, item| {
            acc.push_str(item);
            acc
        },
    )(input)?;
    let (input, comment) = rest(input)?;
    Ok((input, hashline_helper(&ws, &name, &opts, &args, comment)))
}

#[inline]
fn hashline_helper(ws: &str, name: &str, opts: &str, args: &str, comment: &str) -> Hashline {
    use self::Hashline::{OpenEnv, PlainLine};

    // It is ok to unwrap here, since we have checked for UTF-8 when we read the file
    let name_trimmed = name.trim();
    let opts_trimmed = opts.trim();
    let args_trimmed = args.trim();
    let comment_trimmed = comment.trim();

    if args_trimmed.is_empty() {
        // If no args are given, it's an environment
        let env = Environment {
            indent_depth: ws.len(),
            name: name_trimmed.to_string(),
            opts: opts_trimmed.to_string(),
            comment: comment_trimmed.to_string(),
            is_list_like: list_env_parser(name).is_ok(),
        };
        OpenEnv(env)
    } else {
        // If there are some args, it's a single-line command
        let ws_trimmed = ws;
        PlainLine(format!(
            r"{indent}\{name}{opts}{{{args}}}{comment_sep}{comment}",
            indent = ws_trimmed,
            name = name_trimmed,
            opts = opts_trimmed,
            args = args_trimmed,
            comment_sep = if comment_trimmed.is_empty() { "" } else { " " },
            comment = comment_trimmed
        ))
    }
}

// Hashline processing
#[inline]
fn process_hashline<T: AsRef<str>>(line: T) -> Option<Hashline> {
    match hashline_parser(line.as_ref()) {
        Ok((_, r)) => Some(r),
        Err(_) => None,
    }
}

// Itemline parsers
fn itemline_parser(input: &str) -> nom::IResult<&str, Hashline> {
    use self::Hashline::PlainLine;

    use nom::bytes::complete::{is_a, tag};
    use nom::combinator::{opt, rest};

    let (input, indent) = opt(is_a(" "))(input)?;
    let (input, _) = tag("*")(input)?;
    let (input, item) = rest(input)?;

    Ok((
        input,
        PlainLine(format!(
            r"{indent}\item{item_sep}{content}",
            indent = indent.unwrap_or(""),
            content = item.trim(),
            item_sep = if item.trim().is_empty() { "" } else { " " }
        )),
    ))
}

// Itemline processing
#[inline]
fn process_itemline<T: AsRef<str>>(line: T) -> Option<Hashline> {
    match itemline_parser(line.as_ref()) {
        Ok((_, r)) => Some(r),
        Err(_) => None,
    }
}

// Fully process line
pub fn process_line<T>(line: T, list_like_active: bool) -> Hashline
where
    T: AsRef<str>,
{
    use self::Hashline::PlainLine;

    match (process_hashline(&line), list_like_active) {
        (Some(r), _) => r,
        (None, true) => {
            process_itemline(&line).unwrap_or_else(|| PlainLine(line.as_ref().to_string()))
        }
        (None, false) => PlainLine(line.as_ref().to_string()),
    }
}

// LCOV_EXCL_START
#[cfg(test)]
mod tests {
    #[test]
    fn hashline_helper_plain_lines() {
        use super::{hashline_helper, Hashline};

        assert_eq!(
            hashline_helper("", "foo", "", "bar", ""),
            Hashline::PlainLine("\\foo{bar}".to_string())
        );
        assert_eq!(
            hashline_helper("  ", "foo", "", "bar", "qux"),
            Hashline::PlainLine("  \\foo{bar} qux".to_string())
        );
        assert_eq!(
            hashline_helper("    ", "foo", "bar", "qux", ""),
            Hashline::PlainLine("    \\foobar{qux}".to_string())
        );
    }

    #[test]
    fn hashline_helper_environments() {
        use super::{hashline_helper, Environment, Hashline};

        let env_ref_1 = Environment {
            indent_depth: 0,
            name: "foo".to_string(),
            opts: "bar".to_string(),
            comment: "".to_string(),
            is_list_like: false,
        };
        assert_eq!(
            hashline_helper("", "foo", "bar", "", ""),
            Hashline::OpenEnv(env_ref_1)
        );

        let env_ref_2 = Environment {
            indent_depth: 2,
            name: "foo".to_string(),
            opts: "".to_string(),
            comment: "bar".to_string(),
            is_list_like: false,
        };
        assert_eq!(
            hashline_helper("  ", "foo", "", "", "bar"),
            Hashline::OpenEnv(env_ref_2)
        );

        let env_ref_3 = Environment {
            indent_depth: 4,
            name: "foo".to_string(),
            opts: "bar".to_string(),
            comment: "qux".to_string(),
            is_list_like: false,
        };
        assert_eq!(
            hashline_helper("    ", "foo", "bar", "", "qux"),
            Hashline::OpenEnv(env_ref_3)
        );

        let env_ref_4 = Environment {
            indent_depth: 0,
            name: "itemize".to_string(),
            opts: "bar".to_string(),
            comment: "qux".to_string(),
            is_list_like: true,
        };
        assert_eq!(
            hashline_helper("", "itemize", "bar", "", "qux"),
            Hashline::OpenEnv(env_ref_4)
        );
    }

    #[test]
    fn process_itemline() {
        use super::{process_itemline, Hashline};

        // Valid itemlines
        assert_eq!(
            process_itemline("*"),
            Some(Hashline::PlainLine("\\item".to_string()))
        );
        assert_eq!(
            process_itemline("*  "),
            Some(Hashline::PlainLine("\\item".to_string()))
        );
        assert_eq!(
            process_itemline("  *"),
            Some(Hashline::PlainLine("  \\item".to_string()))
        );
        assert_eq!(
            process_itemline("  *  "),
            Some(Hashline::PlainLine("  \\item".to_string()))
        );
        assert_eq!(
            process_itemline("* foo"),
            Some(Hashline::PlainLine("\\item foo".to_string()))
        );
        assert_eq!(
            process_itemline("  * bar"),
            Some(Hashline::PlainLine("  \\item bar".to_string()))
        );
        assert_eq!(
            process_itemline("****"),
            Some(Hashline::PlainLine("\\item ***".to_string()))
        );

        // Not an itemline
        assert_eq!(process_itemline("  baz"), None);
        assert_eq!(process_itemline("qux *"), None);
        assert_eq!(process_itemline("  abc * def"), None);
        assert_eq!(process_itemline("  \\*  "), None);
        assert_eq!(process_itemline("\\*  "), None);
    }

    #[test]
    fn environment_methods() {
        use super::Environment;

        let env_1 = Environment {
            indent_depth: 0,
            name: "foo".to_string(),
            opts: "bar".to_string(),
            comment: "% baz".to_string(),
            is_list_like: true,
        };

        assert_eq!(env_1.latex_begin(), "\\begin{foo}bar % baz");
        assert_eq!(env_1.latex_end(), "\\end{foo}");
        assert_eq!(env_1.is_list_like(), true);
        assert_eq!(env_1.indent_depth(), 0);

        let env_2 = Environment {
            indent_depth: 2,
            name: "abc".to_string(),
            opts: "def".to_string(),
            comment: "".to_string(),
            is_list_like: false,
        };

        assert_eq!(env_2.latex_begin(), "  \\begin{abc}def");
        assert_eq!(env_2.latex_end(), "  \\end{abc}");
        assert_eq!(env_2.is_list_like(), false);
        assert_eq!(env_2.indent_depth(), 2);
    }

    #[test]
    fn list_env_parser() {
        use super::list_env_parser;
        use nom::error::ErrorKind::Tag;
        use nom::Err::Error;

        assert_eq!(list_env_parser("itemize"), Ok(("", ())));
        assert_eq!(list_env_parser("enumerate*"), Ok(("*", ())));
        assert_eq!(list_env_parser("    description  *"), Ok(("  *", ())));
        assert_eq!(list_env_parser("item"), Err(Error(("item", Tag))));
        assert_eq!(list_env_parser("   foobar"), Err(Error(("foobar", Tag))));
    }

    #[test]
    fn escaped_colon() {
        use super::escaped_colon;
        use nom::error::ErrorKind::Tag;
        use nom::Err::Error;

        assert_eq!(escaped_colon(r"\:"), Ok(("", ":")));
        assert_eq!(escaped_colon(""), Err(Error(("", Tag))));
        assert_eq!(escaped_colon("ab"), Err(Error(("ab", Tag))));
    }

    #[test]
    fn name_parser() {
        use super::name_parser;
        use nom::error::ErrorKind::IsNot;
        use nom::Err::Error;

        assert_eq!(name_parser("abc"), Ok(("", "abc")));
        assert_eq!(name_parser(r"abc\:"), Ok((r"\:", "abc")));
        assert_eq!(name_parser(r"\:abc"), Ok(("abc", ":")));
        assert_eq!(name_parser(" "), Err(Error((" ", IsNot))));
        assert_eq!(name_parser(""), Err(Error(("", IsNot))));

        for e in vec![":E", "%E", "(E", "[E", "{E", " E", "\tE"] {
            assert_eq!(name_parser(e), Err(Error((e, IsNot))));
        }
    }

    #[test]
    fn opts_parser() {
        use super::opts_parser;
        use nom::error::ErrorKind::IsNot;
        use nom::Err::Error;

        assert_eq!(opts_parser(r"abc"), Ok(("", "abc")));
        assert_eq!(opts_parser(r"\:abc"), Ok(("abc", ":")));
        assert_eq!(opts_parser(r"\%abc"), Ok(("abc", r"\%")));
        assert_eq!(opts_parser(r"(abc"), Ok(("", "(abc")));
        assert_eq!(opts_parser(r"[abc"), Ok(("", "[abc")));
        assert_eq!(opts_parser(r" abc"), Ok(("", " abc")));
        assert_eq!(opts_parser(""), Err(Error(("", IsNot))));

        for e in vec![":E", "%E"] {
            assert_eq!(opts_parser(e), Err(Error((e, IsNot))));
        }
    }

    #[test]
    fn args_parser() {
        use super::args_parser;
        use nom::error::ErrorKind::IsNot;
        use nom::Err::Error;

        assert_eq!(args_parser(r"abc"), Ok(("", "abc")));
        assert_eq!(args_parser(r"\:abc"), Ok((":abc", r"\")));
        assert_eq!(args_parser(r"\%abc"), Ok(("abc", r"\%")));
        assert_eq!(args_parser(r"(abc"), Ok(("", "(abc")));
        assert_eq!(args_parser(r"[abc"), Ok(("", "[abc")));
        assert_eq!(args_parser(r" abc"), Ok(("", " abc")));
        assert_eq!(args_parser(""), Err(Error(("", IsNot))));
        assert_eq!(args_parser("%E"), Err(Error(("%E", IsNot))));
    }
}
// LCOV_EXCL_STOP
<commit_msg>Minor style fixes<commit_after>#[derive(Debug, PartialEq)]
pub enum Hashline {
    OpenEnv(Environment),
    PlainLine(String),
}

#[derive(Debug, PartialEq)]
pub struct Environment {
    indent_depth: usize,
    name: String,
    opts: String,
    comment: String,
    is_list_like: bool,
}

impl Environment {
    pub fn latex_begin(&self) -> String {
        format!(
            r"{dummy:ind$}\begin{{{name}}}{opts}{comment_sep}{comment}",
            name = self.name,
            opts = self.opts,
            comment = self.comment,
            dummy = "",
            ind = self.indent_depth,
            comment_sep = if self.comment.is_empty() { "" } else { " " },
        )
    }

    pub fn latex_end(&self) -> String {
        format!(
            r"{dummy:ind$}\end{{{name}}}",
            name = self.name,
            dummy = "",
            ind = self.indent_depth,
        )
    }

    pub fn indent_depth(&self) -> usize {
        self.indent_depth
    }

    pub fn is_list_like(&self) -> bool {
        self.is_list_like
    }
}

#[inline]
fn list_env_parser(input: &str) -> nom::IResult<&str, ()> {
    use nom::branch::alt;
    use nom::bytes::complete::{is_a, tag};
    use nom::combinator::opt;

    let (input, _) = opt(is_a(" "))(input)?;
    let (input, _) = alt((tag("itemize"), tag("enumerate"), tag("description")))(input)?;
    Ok((input, ()))
}

#[inline]
fn escaped_colon(input: &str) -> nom::IResult<&str, &str> {
    use nom::bytes::complete::tag;
    use nom::sequence::preceded;

    preceded(tag("\\"), tag(":"))(input)
}

#[inline]
fn name_parser(input: &str) -> nom::IResult<&str, &str> {
    use nom::branch::alt;
    use nom::bytes::complete::{is_not, tag};

    alt((escaped_colon, tag("\\"), is_not("\\:%([{ \t")))(input)
}

#[inline]
fn opts_parser(input: &str) -> nom::IResult<&str, &str> {
    use nom::branch::alt;
    use nom::bytes::complete::{is_not, tag};

    alt((escaped_colon, tag("\\%"), tag("\\"), is_not("\\:%")))(input)
}

#[inline]
fn args_parser(input: &str) -> nom::IResult<&str, &str> {
    use nom::branch::alt;
    use nom::bytes::complete::{is_not, tag};

    alt((tag("\\%"), tag("\\"), is_not("\\%")))(input)
}

fn hashline_parser(input: &str) -> nom::IResult<&str, Hashline> {
    use nom::bytes::complete::{is_a, tag};
    use nom::combinator::{opt, rest};
    use nom::multi::{fold_many0, fold_many1};

    let (input, res) = opt(is_a(" "))(input)?;
    let ws = res.unwrap_or("");
    let (input, _) = tag("# ")(input)?;
    let (input, name) = fold_many1(
        name_parser,
        String::with_capacity(input.len()),
        |mut acc: String, item| {
            acc.push_str(item);
            acc
        },
    )(input)?;
    let (input, opts) = fold_many0(
        opts_parser,
        String::with_capacity(input.len()),
        |mut acc: String, item| {
            acc.push_str(item);
            acc
        },
    )(input)?;
    let (input, _) = tag(":")(input)?;
    let (input, args) = fold_many0(
        args_parser,
        String::with_capacity(input.len()),
        |mut acc: String, item| {
            acc.push_str(item);
            acc
        },
    )(input)?;
    let (input, comment) = rest(input)?;
    Ok((input, hashline_helper(&ws, &name, &opts, &args, comment)))
}

#[inline]
fn hashline_helper(ws: &str, name: &str, opts: &str, args: &str, comment: &str) -> Hashline {
    use self::Hashline::{OpenEnv, PlainLine};

    // It is ok to unwrap here, since we have checked for UTF-8 when we read the file
    let name_trimmed = name.trim();
    let opts_trimmed = opts.trim();
    let args_trimmed = args.trim();
    let comment_trimmed = comment.trim();

    if args_trimmed.is_empty() {
        // If no args are given, it's an environment
        let env = Environment {
            indent_depth: ws.len(),
            name: name_trimmed.to_string(),
            opts: opts_trimmed.to_string(),
            comment: comment_trimmed.to_string(),
            is_list_like: list_env_parser(name).is_ok(),
        };
        OpenEnv(env)
    } else {
        // If there are some args, it's a single-line command
        PlainLine(format!(
            r"{indent}\{name}{opts}{{{args}}}{comment_sep}{comment}",
            indent = ws,
            name = name_trimmed,
            opts = opts_trimmed,
            args = args_trimmed,
            comment_sep = if comment_trimmed.is_empty() { "" } else { " " },
            comment = comment_trimmed,
        ))
    }
}

// Hashline processing
#[inline]
fn process_hashline<T: AsRef<str>>(line: T) -> Option<Hashline> {
    match hashline_parser(line.as_ref()) {
        Ok((_, r)) => Some(r),
        Err(_) => None,
    }
}

// Itemline parsers
fn itemline_parser(input: &str) -> nom::IResult<&str, Hashline> {
    use self::Hashline::PlainLine;

    use nom::bytes::complete::{is_a, tag};
    use nom::combinator::{opt, rest};

    let (input, indent) = opt(is_a(" "))(input)?;
    let (input, _) = tag("*")(input)?;
    let (input, item) = rest(input)?;

    Ok((
        input,
        PlainLine(format!(
            r"{indent}\item{item_sep}{content}",
            indent = indent.unwrap_or(""),
            content = item.trim(),
            item_sep = if item.trim().is_empty() { "" } else { " " },
        )),
    ))
}

// Itemline processing
#[inline]
fn process_itemline<T: AsRef<str>>(line: T) -> Option<Hashline> {
    match itemline_parser(line.as_ref()) {
        Ok((_, r)) => Some(r),
        Err(_) => None,
    }
}

// Fully process line
pub fn process_line<T>(line: T, list_like_active: bool) -> Hashline
where
    T: AsRef<str>,
{
    use self::Hashline::PlainLine;

    match (process_hashline(&line), list_like_active) {
        (Some(r), _) => r,
        (None, true) => {
            process_itemline(&line).unwrap_or_else(|| PlainLine(line.as_ref().to_string()))
        }
        (None, false) => PlainLine(line.as_ref().to_string()),
    }
}

// LCOV_EXCL_START
#[cfg(test)]
mod tests {
    #[test]
    fn hashline_helper_plain_lines() {
        use super::{hashline_helper, Hashline};

        assert_eq!(
            hashline_helper("", "foo", "", "bar", ""),
            Hashline::PlainLine("\\foo{bar}".to_string())
        );
        assert_eq!(
            hashline_helper("  ", "foo", "", "bar", "qux"),
            Hashline::PlainLine("  \\foo{bar} qux".to_string())
        );
        assert_eq!(
            hashline_helper("    ", "foo", "bar", "qux", ""),
            Hashline::PlainLine("    \\foobar{qux}".to_string())
        );
    }

    #[test]
    fn hashline_helper_environments() {
        use super::{hashline_helper, Environment, Hashline};

        let env_ref_1 = Environment {
            indent_depth: 0,
            name: "foo".to_string(),
            opts: "bar".to_string(),
            comment: "".to_string(),
            is_list_like: false,
        };
        assert_eq!(
            hashline_helper("", "foo", "bar", "", ""),
            Hashline::OpenEnv(env_ref_1)
        );

        let env_ref_2 = Environment {
            indent_depth: 2,
            name: "foo".to_string(),
            opts: "".to_string(),
            comment: "bar".to_string(),
            is_list_like: false,
        };
        assert_eq!(
            hashline_helper("  ", "foo", "", "", "bar"),
            Hashline::OpenEnv(env_ref_2)
        );

        let env_ref_3 = Environment {
            indent_depth: 4,
            name: "foo".to_string(),
            opts: "bar".to_string(),
            comment: "qux".to_string(),
            is_list_like: false,
        };
        assert_eq!(
            hashline_helper("    ", "foo", "bar", "", "qux"),
            Hashline::OpenEnv(env_ref_3)
        );

        let env_ref_4 = Environment {
            indent_depth: 0,
            name: "itemize".to_string(),
            opts: "bar".to_string(),
            comment: "qux".to_string(),
            is_list_like: true,
        };
        assert_eq!(
            hashline_helper("", "itemize", "bar", "", "qux"),
            Hashline::OpenEnv(env_ref_4)
        );
    }

    #[test]
    fn process_itemline() {
        use super::{process_itemline, Hashline};

        // Valid itemlines
        assert_eq!(
            process_itemline("*"),
            Some(Hashline::PlainLine("\\item".to_string()))
        );
        assert_eq!(
            process_itemline("*  "),
            Some(Hashline::PlainLine("\\item".to_string()))
        );
        assert_eq!(
            process_itemline("  *"),
            Some(Hashline::PlainLine("  \\item".to_string()))
        );
        assert_eq!(
            process_itemline("  *  "),
            Some(Hashline::PlainLine("  \\item".to_string()))
        );
        assert_eq!(
            process_itemline("* foo"),
            Some(Hashline::PlainLine("\\item foo".to_string()))
        );
        assert_eq!(
            process_itemline("  * bar"),
            Some(Hashline::PlainLine("  \\item bar".to_string()))
        );
        assert_eq!(
            process_itemline("****"),
            Some(Hashline::PlainLine("\\item ***".to_string()))
        );

        // Not an itemline
        assert_eq!(process_itemline("  baz"), None);
        assert_eq!(process_itemline("qux *"), None);
        assert_eq!(process_itemline("  abc * def"), None);
        assert_eq!(process_itemline("  \\*  "), None);
        assert_eq!(process_itemline("\\*  "), None);
    }

    #[test]
    fn environment_methods() {
        use super::Environment;

        let env_1 = Environment {
            indent_depth: 0,
            name: "foo".to_string(),
            opts: "bar".to_string(),
            comment: "% baz".to_string(),
            is_list_like: true,
        };

        assert_eq!(env_1.latex_begin(), "\\begin{foo}bar % baz");
        assert_eq!(env_1.latex_end(), "\\end{foo}");
        assert_eq!(env_1.is_list_like(), true);
        assert_eq!(env_1.indent_depth(), 0);

        let env_2 = Environment {
            indent_depth: 2,
            name: "abc".to_string(),
            opts: "def".to_string(),
            comment: "".to_string(),
            is_list_like: false,
        };

        assert_eq!(env_2.latex_begin(), "  \\begin{abc}def");
        assert_eq!(env_2.latex_end(), "  \\end{abc}");
        assert_eq!(env_2.is_list_like(), false);
        assert_eq!(env_2.indent_depth(), 2);
    }

    #[test]
    fn list_env_parser() {
        use super::list_env_parser;
        use nom::error::ErrorKind::Tag;
        use nom::Err::Error;

        assert_eq!(list_env_parser("itemize"), Ok(("", ())));
        assert_eq!(list_env_parser("enumerate*"), Ok(("*", ())));
        assert_eq!(list_env_parser("    description  *"), Ok(("  *", ())));
        assert_eq!(list_env_parser("item"), Err(Error(("item", Tag))));
        assert_eq!(list_env_parser("   foobar"), Err(Error(("foobar", Tag))));
    }

    #[test]
    fn escaped_colon() {
        use super::escaped_colon;
        use nom::error::ErrorKind::Tag;
        use nom::Err::Error;

        assert_eq!(escaped_colon(r"\:"), Ok(("", ":")));
        assert_eq!(escaped_colon(""), Err(Error(("", Tag))));
        assert_eq!(escaped_colon("ab"), Err(Error(("ab", Tag))));
    }

    #[test]
    fn name_parser() {
        use super::name_parser;
        use nom::error::ErrorKind::IsNot;
        use nom::Err::Error;

        assert_eq!(name_parser("abc"), Ok(("", "abc")));
        assert_eq!(name_parser(r"abc\:"), Ok((r"\:", "abc")));
        assert_eq!(name_parser(r"\:abc"), Ok(("abc", ":")));
        assert_eq!(name_parser(" "), Err(Error((" ", IsNot))));
        assert_eq!(name_parser(""), Err(Error(("", IsNot))));

        for e in vec![":E", "%E", "(E", "[E", "{E", " E", "\tE"] {
            assert_eq!(name_parser(e), Err(Error((e, IsNot))));
        }
    }

    #[test]
    fn opts_parser() {
        use super::opts_parser;
        use nom::error::ErrorKind::IsNot;
        use nom::Err::Error;

        assert_eq!(opts_parser(r"abc"), Ok(("", "abc")));
        assert_eq!(opts_parser(r"\:abc"), Ok(("abc", ":")));
        assert_eq!(opts_parser(r"\%abc"), Ok(("abc", r"\%")));
        assert_eq!(opts_parser(r"(abc"), Ok(("", "(abc")));
        assert_eq!(opts_parser(r"[abc"), Ok(("", "[abc")));
        assert_eq!(opts_parser(r" abc"), Ok(("", " abc")));
        assert_eq!(opts_parser(""), Err(Error(("", IsNot))));

        for e in vec![":E", "%E"] {
            assert_eq!(opts_parser(e), Err(Error((e, IsNot))));
        }
    }

    #[test]
    fn args_parser() {
        use super::args_parser;
        use nom::error::ErrorKind::IsNot;
        use nom::Err::Error;

        assert_eq!(args_parser(r"abc"), Ok(("", "abc")));
        assert_eq!(args_parser(r"\:abc"), Ok((":abc", r"\")));
        assert_eq!(args_parser(r"\%abc"), Ok(("abc", r"\%")));
        assert_eq!(args_parser(r"(abc"), Ok(("", "(abc")));
        assert_eq!(args_parser(r"[abc"), Ok(("", "[abc")));
        assert_eq!(args_parser(r" abc"), Ok(("", " abc")));
        assert_eq!(args_parser(""), Err(Error(("", IsNot))));
        assert_eq!(args_parser("%E"), Err(Error(("%E", IsNot))));
    }
}
// LCOV_EXCL_STOP
<|endoftext|>


<commit_before>use codegen::RustWriter;
use std::str;

pub struct Grammar {
	initializer: Option<~str>,
	rules: ~[~Rule]
}

pub enum Item {
	Rule
}

pub struct Rule {
	name: ~str,
	expr: ~Expr,
	ret_type: ~str,
	exported: bool,
}

pub enum Attr {
	AttrDefineParser(~str),
	AttrDefineType(~str)
}

pub struct CharSetCase {
	start: char,
	end: char
}

pub struct TaggedExpr {
	name: Option<~str>,
	expr: ~Expr
}

pub enum Expr {
	AnyCharExpr,
	LiteralExpr(~str),
	CharSetExpr(bool, ~[CharSetCase]),
	RuleExpr(~str),
	SequenceExpr(~[~Expr]),
	ChoiceExpr(~[~Expr]),
	OptionalExpr(~Expr),
	ZeroOrMore(~Expr),
	OneOrMore(~Expr),
	DelimitedExpr(~Expr, ~Expr),
	PosAssertExpr(~Expr),
	NegAssertExpr(~Expr),
	StringifyExpr(~Expr),
	ActionExpr(~[TaggedExpr], ~str),
}

pub fn compile_grammar(w: &RustWriter, grammar: &Grammar) {
	compile_header(w, grammar.initializer.as_ref().map_default("", |s| s.as_slice()));

	for rule in grammar.rules.iter() {
		compile_rule(w, *rule);
	}
}

fn compile_header(w: &RustWriter, header: &str) {
	w.write("// Generated by rust-peg. Do not edit.
extern mod std;
use std::str::{CharRange};
    ");

 	w.write(header);

 	w.write("
#[inline]
fn slice_eq(input: &str, pos: uint, m: &str) -> Result<(uint, ()), uint> {
    let l = m.len();
    if (input.len() >= pos + l && input.slice(pos, pos+l) == m) {
        Ok((pos+l, ()))
    } else {
        Err(pos)
    }
}

#[inline]
fn any_char(input: &str, pos: uint) -> Result<(uint, ()), uint> {
    if input.len() > pos {
        Ok((input.char_range_at(pos).next, ()))
    } else {
        Err(pos)
    }
}

fn pos_to_line(input: &str, pos: uint) -> uint {
	let mut remaining = pos as int;
	let mut lineno: uint = 1;
	for line in input.lines() {
		remaining -= (line.len() as int) + 1;
		if (remaining <= 0) {
			return lineno;
		}
		lineno+=1;
	}
	return lineno;
}
");
}

fn compile_rule(w: &RustWriter, rule: &Rule) {
	w.line("#[allow(unused_variable)]");
	w.def_fn(false, "parse_"+rule.name, "input: &str, pos: uint", "Result<(uint, " + rule.ret_type + ") , uint>", || {
		compile_expr(w, rule.expr, rule.ret_type != ~"()");
	});

	if rule.exported {
		compile_rule_export(w, rule);
	}
}

fn compile_rule_export(w: &RustWriter, rule: &Rule) {
	w.def_fn(true, rule.name, "input: &str", "Result<"+rule.ret_type+", ~str>", || {
		w.match_block("parse_"+rule.name+"(input, 0)", || {
			w.match_case("Ok((pos, value))", || {
				w.if_else("pos == input.len()",
					|| { w.line("Ok(value)"); },
					|| { w.line("Err(~\"Expected end of input at \" + pos_to_line(input, pos).to_str())"); }
				)
			});
			w.match_inline_case("Err(pos)", "Err(\"Error at \"+ pos_to_line(input, pos).to_str())");
		});
	});
}

fn compile_match_and_then(w: &RustWriter, e: &Expr, value_name: Option<&str>, then: ||) {
	w.let_block("seq_res", || {
		compile_expr(w, e, value_name.is_some());
	});
	w.match_block("seq_res", || {
		w.match_inline_case("Err(pos)", "Err(pos)");
		w.match_case("Ok((pos, "+value_name.unwrap_or("_")+"))", || {
			then();
		});
	});
}

fn compile_zero_or_more(w: &RustWriter, e: &Expr, list_initial: Option<&str>) {
	w.let_mut_stmt("repeat_pos", "pos");
	let result_used = list_initial.is_some();
	if (result_used) {
		w.let_mut_stmt("repeat_value", list_initial.unwrap());
	}
	w.loop_block(|| {
		w.let_block("step_res", || {
			w.let_stmt("pos", "repeat_pos");
			compile_expr(w, e, result_used);
		});
		w.match_block("step_res", || {
			let match_arm = if result_used {
				"Ok((newpos, value))"
			} else {
				"Ok((newpos, _))"
			};
			w.match_case(match_arm, || {
				w.line("repeat_pos = newpos;");
				if result_used {
					w.line("repeat_value.push(value);");
				}
			});
			w.match_case("Err(..)", || {
				w.line("break;");
			});
		});
	});
	if result_used {
		w.line("Ok((repeat_pos, repeat_value))");
	} else {
		w.line("Ok((repeat_pos, ()))");
	}
}

fn compile_expr(w: &RustWriter, e: &Expr, result_used: bool) {
	match *e {
		AnyCharExpr => {
			w.line("any_char(input, pos)");
			/*w.if_else("input.len() > pos",
				||{ w.line("Ok(pos+1)"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		LiteralExpr(ref s) => {
			w.line("slice_eq(input, pos, \""+s.escape_default()+"\")");
			/*w.if_else("slice_eq(input, pos, \""+*s+"\")",
				||{ w.line("Ok(pos+" + s.len().to_str() + ")"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		CharSetExpr(invert, ref cases) => {
			let result_strs = ("Ok((next, ()))", "Err(pos)");
			let (y_str, n_str) = if !invert { result_strs } else { result_strs.swap() };

			w.if_else("input.len() > pos",
				|| {
					w.line("let CharRange {ch, next} = input.char_range_at(pos);");
					w.match_block("ch", || {
						w.write_indent();
						for (i, case) in cases.iter().enumerate() {
							if i != 0 { w.write(" | "); }
							if case.start == case.end {
								w.write("'"+str::from_char(case.start).escape_default()+"'");
							} else {
								let start = str::from_char(case.start).escape_default();
								let end = str::from_char(case.end).escape_default();
								w.write("'"+start+"'..'"+end+"'");
							}
						}
						w.write(" => { "+y_str+" }\n");
						w.match_inline_case("_", n_str);
					});
				},
				|| { w.line("Err(pos)"); }
			)
		}

		RuleExpr(ref ruleName) => {
			w.line("parse_"+*ruleName+"(input, pos)");
		}

		SequenceExpr(ref exprs) => {
			fn write_seq(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0], false);
				} else {
					compile_match_and_then(w, exprs[0], None, || {
						write_seq(w, exprs.tail());
					});
				}
			}

			if (exprs.len() > 0 ) {
				write_seq(w, *exprs);
			}
		}

		ChoiceExpr(ref exprs) => {
			fn write_choice(w: &RustWriter, exprs: &[~Expr], result_used: bool) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0], result_used);
				} else {
					w.let_block("choice_res", || {
						compile_expr(w, exprs[0], result_used);
					});
					w.match_block("choice_res", || {
						w.match_inline_case("Ok((pos, value))", "Ok((pos, value))");
						w.match_case("Err(..)", || {
							write_choice(w, exprs.tail(), result_used);
						});
					});
				}
			}

			if (exprs.len() > 0 ) {
				write_choice(w, *exprs, result_used);
			}
		}

		OptionalExpr(ref e) => {
			w.let_block("optional_res", || {
				compile_expr(w, *e, result_used);
			});
			w.match_block("optional_res", || {
				w.match_inline_case("Ok((newpos, value))", "Ok((newpos, Some(value)))");
				w.match_inline_case("Err(..)", "Ok((pos, None))");
			});
		}

		ZeroOrMore(ref e) => {
			compile_zero_or_more(w, *e, if result_used { Some("~[]") } else { None });
		}

		OneOrMore(ref e) => {
			compile_match_and_then(w, *e, if result_used { Some("first_value") } else { None }, || {
				compile_zero_or_more(w, *e, if result_used { Some("~[first_value]") } else { None });
			});
		}

		DelimitedExpr(_, _) => fail!("not implemented"),
		StringifyExpr(..) => fail!("not implemented"),

		PosAssertExpr(ref e) => {
			w.let_block("assert_res", || {
				compile_expr(w, *e, false);
			});
			w.match_block("assert_res", || {
				w.match_inline_case("Ok(..)", "Ok((pos, ()))");
				w.match_inline_case("Err(..)", "Err(pos)");
			});
		}

		NegAssertExpr(ref e) => {
			w.let_block("neg_assert_res", || {
				compile_expr(w, *e, false);
			});
			w.match_block("neg_assert_res", || {
				w.match_inline_case("Err(..)", "Ok((pos, ()))");
				w.match_inline_case("Ok(..)", "Err(pos)");
			});
		}

		ActionExpr(ref exprs, ref code) => {
			w.let_stmt("start_pos", "pos");
			fn write_seq(w: &RustWriter, exprs: &[TaggedExpr], code: &str) {
				if (exprs.len() > 0) {
					let name = exprs.head().name.as_ref().map(|s| s.as_slice());
					compile_match_and_then(w, exprs.head().expr, name, || {
						write_seq(w, exprs.tail(), code);
					});
				} else {
					w.let_stmt("match_str",  "input.slice(start_pos, pos);");
					w.write_indent();
					w.write("Ok((pos, {");
					w.write(code);
					w.write("}))\n");
				}
			}

			write_seq(w, *exprs, *code);
		}
	}
}

<commit_msg>Remove dead code<commit_after>use codegen::RustWriter;
use std::str;

pub struct Grammar {
	initializer: Option<~str>,
	rules: ~[~Rule]
}

pub struct Rule {
	name: ~str,
	expr: ~Expr,
	ret_type: ~str,
	exported: bool,
}

pub struct CharSetCase {
	start: char,
	end: char
}

pub struct TaggedExpr {
	name: Option<~str>,
	expr: ~Expr
}

pub enum Expr {
	AnyCharExpr,
	LiteralExpr(~str),
	CharSetExpr(bool, ~[CharSetCase]),
	RuleExpr(~str),
	SequenceExpr(~[~Expr]),
	ChoiceExpr(~[~Expr]),
	OptionalExpr(~Expr),
	ZeroOrMore(~Expr),
	OneOrMore(~Expr),
	DelimitedExpr(~Expr, ~Expr),
	PosAssertExpr(~Expr),
	NegAssertExpr(~Expr),
	StringifyExpr(~Expr),
	ActionExpr(~[TaggedExpr], ~str),
}

pub fn compile_grammar(w: &RustWriter, grammar: &Grammar) {
	compile_header(w, grammar.initializer.as_ref().map_default("", |s| s.as_slice()));

	for rule in grammar.rules.iter() {
		compile_rule(w, *rule);
	}
}

fn compile_header(w: &RustWriter, header: &str) {
	w.write("// Generated by rust-peg. Do not edit.
extern mod std;
use std::str::{CharRange};
    ");

 	w.write(header);

 	w.write("
#[inline]
fn slice_eq(input: &str, pos: uint, m: &str) -> Result<(uint, ()), uint> {
    let l = m.len();
    if (input.len() >= pos + l && input.slice(pos, pos+l) == m) {
        Ok((pos+l, ()))
    } else {
        Err(pos)
    }
}

#[inline]
fn any_char(input: &str, pos: uint) -> Result<(uint, ()), uint> {
    if input.len() > pos {
        Ok((input.char_range_at(pos).next, ()))
    } else {
        Err(pos)
    }
}

fn pos_to_line(input: &str, pos: uint) -> uint {
	let mut remaining = pos as int;
	let mut lineno: uint = 1;
	for line in input.lines() {
		remaining -= (line.len() as int) + 1;
		if (remaining <= 0) {
			return lineno;
		}
		lineno+=1;
	}
	return lineno;
}
");
}

fn compile_rule(w: &RustWriter, rule: &Rule) {
	w.line("#[allow(unused_variable)]");
	w.def_fn(false, "parse_"+rule.name, "input: &str, pos: uint", "Result<(uint, " + rule.ret_type + ") , uint>", || {
		compile_expr(w, rule.expr, rule.ret_type != ~"()");
	});

	if rule.exported {
		compile_rule_export(w, rule);
	}
}

fn compile_rule_export(w: &RustWriter, rule: &Rule) {
	w.def_fn(true, rule.name, "input: &str", "Result<"+rule.ret_type+", ~str>", || {
		w.match_block("parse_"+rule.name+"(input, 0)", || {
			w.match_case("Ok((pos, value))", || {
				w.if_else("pos == input.len()",
					|| { w.line("Ok(value)"); },
					|| { w.line("Err(~\"Expected end of input at \" + pos_to_line(input, pos).to_str())"); }
				)
			});
			w.match_inline_case("Err(pos)", "Err(\"Error at \"+ pos_to_line(input, pos).to_str())");
		});
	});
}

fn compile_match_and_then(w: &RustWriter, e: &Expr, value_name: Option<&str>, then: ||) {
	w.let_block("seq_res", || {
		compile_expr(w, e, value_name.is_some());
	});
	w.match_block("seq_res", || {
		w.match_inline_case("Err(pos)", "Err(pos)");
		w.match_case("Ok((pos, "+value_name.unwrap_or("_")+"))", || {
			then();
		});
	});
}

fn compile_zero_or_more(w: &RustWriter, e: &Expr, list_initial: Option<&str>) {
	w.let_mut_stmt("repeat_pos", "pos");
	let result_used = list_initial.is_some();
	if (result_used) {
		w.let_mut_stmt("repeat_value", list_initial.unwrap());
	}
	w.loop_block(|| {
		w.let_block("step_res", || {
			w.let_stmt("pos", "repeat_pos");
			compile_expr(w, e, result_used);
		});
		w.match_block("step_res", || {
			let match_arm = if result_used {
				"Ok((newpos, value))"
			} else {
				"Ok((newpos, _))"
			};
			w.match_case(match_arm, || {
				w.line("repeat_pos = newpos;");
				if result_used {
					w.line("repeat_value.push(value);");
				}
			});
			w.match_case("Err(..)", || {
				w.line("break;");
			});
		});
	});
	if result_used {
		w.line("Ok((repeat_pos, repeat_value))");
	} else {
		w.line("Ok((repeat_pos, ()))");
	}
}

fn compile_expr(w: &RustWriter, e: &Expr, result_used: bool) {
	match *e {
		AnyCharExpr => {
			w.line("any_char(input, pos)");
			/*w.if_else("input.len() > pos",
				||{ w.line("Ok(pos+1)"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		LiteralExpr(ref s) => {
			w.line("slice_eq(input, pos, \""+s.escape_default()+"\")");
			/*w.if_else("slice_eq(input, pos, \""+*s+"\")",
				||{ w.line("Ok(pos+" + s.len().to_str() + ")"); },
				||{ w.line("Err(pos)"); }
			);*/
		}

		CharSetExpr(invert, ref cases) => {
			let result_strs = ("Ok((next, ()))", "Err(pos)");
			let (y_str, n_str) = if !invert { result_strs } else { result_strs.swap() };

			w.if_else("input.len() > pos",
				|| {
					w.line("let CharRange {ch, next} = input.char_range_at(pos);");
					w.match_block("ch", || {
						w.write_indent();
						for (i, case) in cases.iter().enumerate() {
							if i != 0 { w.write(" | "); }
							if case.start == case.end {
								w.write("'"+str::from_char(case.start).escape_default()+"'");
							} else {
								let start = str::from_char(case.start).escape_default();
								let end = str::from_char(case.end).escape_default();
								w.write("'"+start+"'..'"+end+"'");
							}
						}
						w.write(" => { "+y_str+" }\n");
						w.match_inline_case("_", n_str);
					});
				},
				|| { w.line("Err(pos)"); }
			)
		}

		RuleExpr(ref ruleName) => {
			w.line("parse_"+*ruleName+"(input, pos)");
		}

		SequenceExpr(ref exprs) => {
			fn write_seq(w: &RustWriter, exprs: &[~Expr]) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0], false);
				} else {
					compile_match_and_then(w, exprs[0], None, || {
						write_seq(w, exprs.tail());
					});
				}
			}

			if (exprs.len() > 0 ) {
				write_seq(w, *exprs);
			}
		}

		ChoiceExpr(ref exprs) => {
			fn write_choice(w: &RustWriter, exprs: &[~Expr], result_used: bool) {
				if (exprs.len() == 1) {
					compile_expr(w, exprs[0], result_used);
				} else {
					w.let_block("choice_res", || {
						compile_expr(w, exprs[0], result_used);
					});
					w.match_block("choice_res", || {
						w.match_inline_case("Ok((pos, value))", "Ok((pos, value))");
						w.match_case("Err(..)", || {
							write_choice(w, exprs.tail(), result_used);
						});
					});
				}
			}

			if (exprs.len() > 0 ) {
				write_choice(w, *exprs, result_used);
			}
		}

		OptionalExpr(ref e) => {
			w.let_block("optional_res", || {
				compile_expr(w, *e, result_used);
			});
			w.match_block("optional_res", || {
				w.match_inline_case("Ok((newpos, value))", "Ok((newpos, Some(value)))");
				w.match_inline_case("Err(..)", "Ok((pos, None))");
			});
		}

		ZeroOrMore(ref e) => {
			compile_zero_or_more(w, *e, if result_used { Some("~[]") } else { None });
		}

		OneOrMore(ref e) => {
			compile_match_and_then(w, *e, if result_used { Some("first_value") } else { None }, || {
				compile_zero_or_more(w, *e, if result_used { Some("~[first_value]") } else { None });
			});
		}

		DelimitedExpr(_, _) => fail!("not implemented"),
		StringifyExpr(..) => fail!("not implemented"),

		PosAssertExpr(ref e) => {
			w.let_block("assert_res", || {
				compile_expr(w, *e, false);
			});
			w.match_block("assert_res", || {
				w.match_inline_case("Ok(..)", "Ok((pos, ()))");
				w.match_inline_case("Err(..)", "Err(pos)");
			});
		}

		NegAssertExpr(ref e) => {
			w.let_block("neg_assert_res", || {
				compile_expr(w, *e, false);
			});
			w.match_block("neg_assert_res", || {
				w.match_inline_case("Err(..)", "Ok((pos, ()))");
				w.match_inline_case("Ok(..)", "Err(pos)");
			});
		}

		ActionExpr(ref exprs, ref code) => {
			w.let_stmt("start_pos", "pos");
			fn write_seq(w: &RustWriter, exprs: &[TaggedExpr], code: &str) {
				if (exprs.len() > 0) {
					let name = exprs.head().name.as_ref().map(|s| s.as_slice());
					compile_match_and_then(w, exprs.head().expr, name, || {
						write_seq(w, exprs.tail(), code);
					});
				} else {
					w.let_stmt("match_str",  "input.slice(start_pos, pos);");
					w.write_indent();
					w.write("Ok((pos, {");
					w.write(code);
					w.write("}))\n");
				}
			}

			write_seq(w, *exprs, *code);
		}
	}
}

<|endoftext|>


<commit_before>//! The module contains the implementation of HTTP/2 frames.

/// A helper macro that unpacks a sequence of 4 bytes found in the buffer with
/// the given identifier, starting at the given offset, into the given integer
/// type. Obviously, the integer type should be able to support at least 4
/// bytes.
///
/// # Examples
///
/// ```rust
/// let buf: [u8; 4] = [0, 0, 0, 1];
/// assert_eq!(1u32, unpack_octets_4!(buf, 0, u32));
/// ```
macro_rules! unpack_octets_4 {
    ($buf:ident, $offset:expr, $tip:ty) => (
        (($buf[$offset + 0] as $tip) << 24) |
        (($buf[$offset + 1] as $tip) << 16) |
        (($buf[$offset + 2] as $tip) <<  8) |
        (($buf[$offset + 3] as $tip) <<  0)
    );
}

/// An alias for the 9-byte buffer that each HTTP/2 frame header must be stored
/// in.
type FrameHeaderBuffer = [u8; 9];
/// An alias for the 4-tuple representing the components of each HTTP/2 frame
/// header.
type FrameHeader = (u32, u8, u8, u32);

/// Deconstructs a `FrameHeader` into its corresponding 4 components,
/// represented as a 4-tuple: `(length, frame_type, flags, stream_id)`.
///
/// The frame `type` and `flags` components are returned as their original
/// octet representation, rather than reinterpreted.
fn unpack_header(header: &FrameHeaderBuffer) -> FrameHeader {
    let length: u32 = (
        ((header[0] as u32) << 16) |
        ((header[1] as u32) <<  8) |
        ((header[2] as u32) <<  0));
    let frame_type = header[3];
    let flags = header[4];
    let stream_id: u32 = unpack_octets_4!(header, 5, u32);

    (length, frame_type, flags, stream_id)
}

/// Constructs a buffer of 9 bytes that represents the given `FrameHeader`.
fn pack_header(header: &FrameHeader) -> FrameHeaderBuffer {
    let &(length, frame_type, flags, stream_id) = header;

    [
        (((length >> 16) & 0x000000FF) as u8),
        (((length >>  8) & 0x000000FF) as u8),
        (((length >>  0) & 0x000000FF) as u8),
        frame_type,
        flags,
        (((stream_id >> 24) & 0x000000FF) as u8),
        (((stream_id >> 16) & 0x000000FF) as u8),
        (((stream_id >>  8) & 0x000000FF) as u8),
        (((stream_id >>  0) & 0x000000FF) as u8),
    ]
}

/// An enum that lists all valid settings that can be sent in a SETTINGS
/// frame.
///
/// Each setting has a value that is a 32 bit unsigned integer (6.5.1.).
#[derive(Clone)]
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Copy)]
pub enum HttpSetting {
    HeaderTableSize(u32),
    EnablePush(u32),
    MaxConcurrentStreams(u32),
    InitialWindowSize(u32),
    MaxFrameSize(u32),
    MaxHeaderListSize(u32),
}

impl HttpSetting {
    /// Creates a new `HttpSetting` with the correct variant corresponding to
    /// the given setting id, based on the settings IDs defined in section
    /// 6.5.2.
    pub fn from_id(id: u16, val: u32) -> Option<HttpSetting> {
        match id {
            1 => Some(HttpSetting::HeaderTableSize(val)),
            2 => Some(HttpSetting::EnablePush(val)),
            3 => Some(HttpSetting::MaxConcurrentStreams(val)),
            4 => Some(HttpSetting::InitialWindowSize(val)),
            5 => Some(HttpSetting::MaxFrameSize(val)),
            6 => Some(HttpSetting::MaxHeaderListSize(val)),
            _ => None,
        }
    }

    /// Creates a new `HttpSetting` by parsing the given buffer of 6 bytes,
    /// which contains the raw byte representation of the setting, according
    /// to the "SETTINGS format" defined in section 6.5.1.
    ///
    /// The `raw_setting` parameter should have length at least 6 bytes, since
    /// the length of the raw setting is exactly 6 bytes.
    ///
    /// # Panics
    ///
    /// If given a buffer shorter than 6 bytes, the function will panic.
    fn parse_setting(raw_setting: &[u8]) -> Option<HttpSetting> {
        let id: u16 = ((raw_setting[0] as u16) << 8) | (raw_setting[1] as u16);
        let val: u32 = unpack_octets_4!(raw_setting, 2, u32);

        HttpSetting::from_id(id, val)
    }

    /// Returns the setting ID as an unsigned 16 bit integer, as defined in
    /// section 6.5.2.
    pub fn get_id(&self) -> u16 {
        match self {
            &HttpSetting::HeaderTableSize(_) => 1,
            &HttpSetting::EnablePush(_) => 2,
            &HttpSetting::MaxConcurrentStreams(_) => 3,
            &HttpSetting::InitialWindowSize(_) => 4,
            &HttpSetting::MaxFrameSize(_) => 5,
            &HttpSetting::MaxHeaderListSize(_) => 6,
        }
    }

    /// Gets the setting value by unpacking it from the wrapped `u32`.
    pub fn get_val(&self) -> u32 {
        match self {
            &HttpSetting::HeaderTableSize(ref val) => val.clone(),
            &HttpSetting::EnablePush(ref val) => val.clone(),
            &HttpSetting::MaxConcurrentStreams(ref val) => val.clone(),
            &HttpSetting::InitialWindowSize(ref val) => val.clone(),
            &HttpSetting::MaxFrameSize(ref val) => val.clone(),
            &HttpSetting::MaxHeaderListSize(ref val) => val.clone(),
        }
    }

    /// Serializes a setting into its "on-the-wire" representation of 6 octets,
    /// according to section 6.5.1.
    fn serialize(&self) -> [u8; 6] {
        let (id, val) = (self.get_id(), self.get_val());
        [
            ((id >> 8) & 0x00FF) as u8,
            ((id >> 0) & 0x00FF) as u8,
            (((val >> 24) & 0x000000FF) as u8),
            (((val >> 16) & 0x000000FF) as u8),
            (((val >>  8) & 0x000000FF) as u8),
            (((val >>  0) & 0x000000FF) as u8),
        ]
    }
}

#[cfg(test)]
mod tests {
    use super::{
        unpack_header,
        pack_header,
        HttpSetting,
    };

    /// Tests that the `unpack_header` function correctly returns the
    /// components of HTTP/2 frame headers.
    #[test]
    fn test_unpack_header() {
        {
            let header = [0; 9];
            assert_eq!((0, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 2, 3, 0, 0, 0, 4];
            assert_eq!((1, 2, 3, 4), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 200, 100, 0, 0, 0, 4];
            assert_eq!((1, 200, 100, 4), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 0, 0, 0, 0, 0, 0];
            assert_eq!((1, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 1, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!((256, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [1, 0, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!((256 * 256, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 0, 0, 0, 0, 0, 0, 0, 1];
            assert_eq!((0, 0, 0, 1), unpack_header(&header));
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1];
            assert_eq!(((1 << 24) - 1, 0, 0, 1), unpack_header(&header));
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1];
            assert_eq!(
                ((1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)),
                unpack_header(&header));
        }
    }

    /// Tests that the `pack_header` function correctly returns the buffer
    /// corresponding to components of HTTP/2 frame headers.
    #[test]
    fn test_pack_header() {
        {
            let header = [0; 9];
            assert_eq!(pack_header(&(0, 0, 0, 0)), header);
        }
        {
            let header = [0, 0, 1, 2, 3, 0, 0, 0, 4];
            assert_eq!(pack_header(&(1, 2, 3, 4)), header);
        }
        {
            let header = [0, 0, 1, 200, 100, 0, 0, 0, 4];
            assert_eq!(pack_header(&(1, 200, 100, 4)), header);
        }
        {
            let header = [0, 0, 1, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(1, 0, 0, 0)), header);
        }
        {
            let header = [0, 1, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(256, 0, 0, 0)), header);
        }
        {
            let header = [1, 0, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(256 * 256, 0, 0, 0)), header);
        }
        {
            let header = [0, 0, 0, 0, 0, 0, 0, 0, 1];
            assert_eq!(pack_header(&(0, 0, 0, 1)), header);
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1];
            assert_eq!(pack_header(&((1 << 24) - 1, 0, 0, 1)), header);
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1];
            let header_components = (
                (1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)
            );
            assert_eq!(pack_header(&header_components), header);
        }
    }

    /// Tests that the `HttpSetting::parse_setting` method correctly creates
    /// settings from raw bytes.
    #[test]
    fn test_setting_deserialize() {
        {
            let buf = [0, 1, 0, 0, 1, 0];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::HeaderTableSize(1 << 8));
        }
        {
            let buf = [0, 2, 0, 0, 0, 1];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::EnablePush(1));
        }
        {
            let buf = [0, 3, 0, 0, 0, 0];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::MaxConcurrentStreams(0));
        }
        {
            let buf = [0, 4, 0, 0, 0, 1];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::InitialWindowSize(1));
        }
        {
            let buf = [0, 5, 0, 0, 0, 255];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::MaxFrameSize((1 << 8) - 1));
        }
        {
            let buf = [0, 6, 0, 0, 0, 255];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::MaxHeaderListSize((1 << 8) - 1));
        }
        {
            let buf = [0, 7, 0, 0, 0, 255];

            let setting = HttpSetting::parse_setting(&buf);

            assert!(setting.is_none());
        }
        {
            let buf = [0, 0, 0, 0, 0, 255];

            let setting = HttpSetting::parse_setting(&buf);

            assert!(setting.is_none());
        }
    }

    /// Tests that the `HttpSetting::serialize` method correctly creates
    /// a 6 byte buffer based on the given setting.
    #[test]
    fn test_setting_serialize() {
        {
            let buf = [0, 1, 0, 0, 1, 0];

            let setting = HttpSetting::HeaderTableSize(1 << 8);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 2, 0, 0, 0, 1];

            let setting = HttpSetting::EnablePush(1);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 3, 0, 0, 0, 0];

            let setting = HttpSetting::MaxConcurrentStreams(0);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 4, 0, 0, 0, 1];

            let setting = HttpSetting::InitialWindowSize(1);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 5, 0, 0, 0, 255];

            let setting = HttpSetting::MaxFrameSize((1 << 8) - 1);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 6, 0, 0, 0, 255];

            let setting = HttpSetting::MaxHeaderListSize((1 << 8) - 1);

            assert_eq!(buf, setting.serialize());
        }
    }
}
<commit_msg>Make the `FrameHeader` type public<commit_after>//! The module contains the implementation of HTTP/2 frames.

/// A helper macro that unpacks a sequence of 4 bytes found in the buffer with
/// the given identifier, starting at the given offset, into the given integer
/// type. Obviously, the integer type should be able to support at least 4
/// bytes.
///
/// # Examples
///
/// ```rust
/// let buf: [u8; 4] = [0, 0, 0, 1];
/// assert_eq!(1u32, unpack_octets_4!(buf, 0, u32));
/// ```
macro_rules! unpack_octets_4 {
    ($buf:ident, $offset:expr, $tip:ty) => (
        (($buf[$offset + 0] as $tip) << 24) |
        (($buf[$offset + 1] as $tip) << 16) |
        (($buf[$offset + 2] as $tip) <<  8) |
        (($buf[$offset + 3] as $tip) <<  0)
    );
}

/// An alias for the 9-byte buffer that each HTTP/2 frame header must be stored
/// in.
type FrameHeaderBuffer = [u8; 9];
/// An alias for the 4-tuple representing the components of each HTTP/2 frame
/// header.
pub type FrameHeader = (u32, u8, u8, u32);

/// Deconstructs a `FrameHeader` into its corresponding 4 components,
/// represented as a 4-tuple: `(length, frame_type, flags, stream_id)`.
///
/// The frame `type` and `flags` components are returned as their original
/// octet representation, rather than reinterpreted.
fn unpack_header(header: &FrameHeaderBuffer) -> FrameHeader {
    let length: u32 = (
        ((header[0] as u32) << 16) |
        ((header[1] as u32) <<  8) |
        ((header[2] as u32) <<  0));
    let frame_type = header[3];
    let flags = header[4];
    let stream_id: u32 = unpack_octets_4!(header, 5, u32);

    (length, frame_type, flags, stream_id)
}

/// Constructs a buffer of 9 bytes that represents the given `FrameHeader`.
fn pack_header(header: &FrameHeader) -> FrameHeaderBuffer {
    let &(length, frame_type, flags, stream_id) = header;

    [
        (((length >> 16) & 0x000000FF) as u8),
        (((length >>  8) & 0x000000FF) as u8),
        (((length >>  0) & 0x000000FF) as u8),
        frame_type,
        flags,
        (((stream_id >> 24) & 0x000000FF) as u8),
        (((stream_id >> 16) & 0x000000FF) as u8),
        (((stream_id >>  8) & 0x000000FF) as u8),
        (((stream_id >>  0) & 0x000000FF) as u8),
    ]
}

/// An enum that lists all valid settings that can be sent in a SETTINGS
/// frame.
///
/// Each setting has a value that is a 32 bit unsigned integer (6.5.1.).
#[derive(Clone)]
#[derive(PartialEq)]
#[derive(Debug)]
#[derive(Copy)]
pub enum HttpSetting {
    HeaderTableSize(u32),
    EnablePush(u32),
    MaxConcurrentStreams(u32),
    InitialWindowSize(u32),
    MaxFrameSize(u32),
    MaxHeaderListSize(u32),
}

impl HttpSetting {
    /// Creates a new `HttpSetting` with the correct variant corresponding to
    /// the given setting id, based on the settings IDs defined in section
    /// 6.5.2.
    pub fn from_id(id: u16, val: u32) -> Option<HttpSetting> {
        match id {
            1 => Some(HttpSetting::HeaderTableSize(val)),
            2 => Some(HttpSetting::EnablePush(val)),
            3 => Some(HttpSetting::MaxConcurrentStreams(val)),
            4 => Some(HttpSetting::InitialWindowSize(val)),
            5 => Some(HttpSetting::MaxFrameSize(val)),
            6 => Some(HttpSetting::MaxHeaderListSize(val)),
            _ => None,
        }
    }

    /// Creates a new `HttpSetting` by parsing the given buffer of 6 bytes,
    /// which contains the raw byte representation of the setting, according
    /// to the "SETTINGS format" defined in section 6.5.1.
    ///
    /// The `raw_setting` parameter should have length at least 6 bytes, since
    /// the length of the raw setting is exactly 6 bytes.
    ///
    /// # Panics
    ///
    /// If given a buffer shorter than 6 bytes, the function will panic.
    fn parse_setting(raw_setting: &[u8]) -> Option<HttpSetting> {
        let id: u16 = ((raw_setting[0] as u16) << 8) | (raw_setting[1] as u16);
        let val: u32 = unpack_octets_4!(raw_setting, 2, u32);

        HttpSetting::from_id(id, val)
    }

    /// Returns the setting ID as an unsigned 16 bit integer, as defined in
    /// section 6.5.2.
    pub fn get_id(&self) -> u16 {
        match self {
            &HttpSetting::HeaderTableSize(_) => 1,
            &HttpSetting::EnablePush(_) => 2,
            &HttpSetting::MaxConcurrentStreams(_) => 3,
            &HttpSetting::InitialWindowSize(_) => 4,
            &HttpSetting::MaxFrameSize(_) => 5,
            &HttpSetting::MaxHeaderListSize(_) => 6,
        }
    }

    /// Gets the setting value by unpacking it from the wrapped `u32`.
    pub fn get_val(&self) -> u32 {
        match self {
            &HttpSetting::HeaderTableSize(ref val) => val.clone(),
            &HttpSetting::EnablePush(ref val) => val.clone(),
            &HttpSetting::MaxConcurrentStreams(ref val) => val.clone(),
            &HttpSetting::InitialWindowSize(ref val) => val.clone(),
            &HttpSetting::MaxFrameSize(ref val) => val.clone(),
            &HttpSetting::MaxHeaderListSize(ref val) => val.clone(),
        }
    }

    /// Serializes a setting into its "on-the-wire" representation of 6 octets,
    /// according to section 6.5.1.
    fn serialize(&self) -> [u8; 6] {
        let (id, val) = (self.get_id(), self.get_val());
        [
            ((id >> 8) & 0x00FF) as u8,
            ((id >> 0) & 0x00FF) as u8,
            (((val >> 24) & 0x000000FF) as u8),
            (((val >> 16) & 0x000000FF) as u8),
            (((val >>  8) & 0x000000FF) as u8),
            (((val >>  0) & 0x000000FF) as u8),
        ]
    }
}

#[cfg(test)]
mod tests {
    use super::{
        unpack_header,
        pack_header,
        HttpSetting,
    };

    /// Tests that the `unpack_header` function correctly returns the
    /// components of HTTP/2 frame headers.
    #[test]
    fn test_unpack_header() {
        {
            let header = [0; 9];
            assert_eq!((0, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 2, 3, 0, 0, 0, 4];
            assert_eq!((1, 2, 3, 4), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 200, 100, 0, 0, 0, 4];
            assert_eq!((1, 200, 100, 4), unpack_header(&header));
        }
        {
            let header = [0, 0, 1, 0, 0, 0, 0, 0, 0];
            assert_eq!((1, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 1, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!((256, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [1, 0, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!((256 * 256, 0, 0, 0), unpack_header(&header));
        }
        {
            let header = [0, 0, 0, 0, 0, 0, 0, 0, 1];
            assert_eq!((0, 0, 0, 1), unpack_header(&header));
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1];
            assert_eq!(((1 << 24) - 1, 0, 0, 1), unpack_header(&header));
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1];
            assert_eq!(
                ((1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)),
                unpack_header(&header));
        }
    }

    /// Tests that the `pack_header` function correctly returns the buffer
    /// corresponding to components of HTTP/2 frame headers.
    #[test]
    fn test_pack_header() {
        {
            let header = [0; 9];
            assert_eq!(pack_header(&(0, 0, 0, 0)), header);
        }
        {
            let header = [0, 0, 1, 2, 3, 0, 0, 0, 4];
            assert_eq!(pack_header(&(1, 2, 3, 4)), header);
        }
        {
            let header = [0, 0, 1, 200, 100, 0, 0, 0, 4];
            assert_eq!(pack_header(&(1, 200, 100, 4)), header);
        }
        {
            let header = [0, 0, 1, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(1, 0, 0, 0)), header);
        }
        {
            let header = [0, 1, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(256, 0, 0, 0)), header);
        }
        {
            let header = [1, 0, 0, 0, 0, 0, 0, 0, 0];
            assert_eq!(pack_header(&(256 * 256, 0, 0, 0)), header);
        }
        {
            let header = [0, 0, 0, 0, 0, 0, 0, 0, 1];
            assert_eq!(pack_header(&(0, 0, 0, 1)), header);
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 0, 0, 0, 1];
            assert_eq!(pack_header(&((1 << 24) - 1, 0, 0, 1)), header);
        }
        {
            let header = [0xFF, 0xFF, 0xFF, 0, 0, 1, 1, 1, 1];
            let header_components = (
                (1 << 24) - 1, 0, 0, 1 + (1 << 8) + (1 << 16) + (1 << 24)
            );
            assert_eq!(pack_header(&header_components), header);
        }
    }

    /// Tests that the `HttpSetting::parse_setting` method correctly creates
    /// settings from raw bytes.
    #[test]
    fn test_setting_deserialize() {
        {
            let buf = [0, 1, 0, 0, 1, 0];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::HeaderTableSize(1 << 8));
        }
        {
            let buf = [0, 2, 0, 0, 0, 1];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::EnablePush(1));
        }
        {
            let buf = [0, 3, 0, 0, 0, 0];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::MaxConcurrentStreams(0));
        }
        {
            let buf = [0, 4, 0, 0, 0, 1];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::InitialWindowSize(1));
        }
        {
            let buf = [0, 5, 0, 0, 0, 255];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::MaxFrameSize((1 << 8) - 1));
        }
        {
            let buf = [0, 6, 0, 0, 0, 255];

            let setting = HttpSetting::parse_setting(&buf).unwrap();

            assert_eq!(setting, HttpSetting::MaxHeaderListSize((1 << 8) - 1));
        }
        {
            let buf = [0, 7, 0, 0, 0, 255];

            let setting = HttpSetting::parse_setting(&buf);

            assert!(setting.is_none());
        }
        {
            let buf = [0, 0, 0, 0, 0, 255];

            let setting = HttpSetting::parse_setting(&buf);

            assert!(setting.is_none());
        }
    }

    /// Tests that the `HttpSetting::serialize` method correctly creates
    /// a 6 byte buffer based on the given setting.
    #[test]
    fn test_setting_serialize() {
        {
            let buf = [0, 1, 0, 0, 1, 0];

            let setting = HttpSetting::HeaderTableSize(1 << 8);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 2, 0, 0, 0, 1];

            let setting = HttpSetting::EnablePush(1);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 3, 0, 0, 0, 0];

            let setting = HttpSetting::MaxConcurrentStreams(0);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 4, 0, 0, 0, 1];

            let setting = HttpSetting::InitialWindowSize(1);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 5, 0, 0, 0, 255];

            let setting = HttpSetting::MaxFrameSize((1 << 8) - 1);

            assert_eq!(buf, setting.serialize());
        }
        {
            let buf = [0, 6, 0, 0, 0, 255];

            let setting = HttpSetting::MaxHeaderListSize((1 << 8) - 1);

            assert_eq!(buf, setting.serialize());
        }
    }
}
<|endoftext|>


<commit_before>use std::env;

use std::io::Read;
use serde::{Serialize, Deserialize};
use reqwest::{Client, Error};
use reqwest::header::{Headers, ContentType};
use std::str;
use std::collections::HashMap;
use serde_json;

#[derive(Debug, Clone, Serialize)]
pub struct ExecuteRequest {
    channel: String,
    mode: String,
    #[serde(rename = "crateType")]
    crate_type: String,
    tests: bool,
    code: String,
}
impl ExecuteRequest {
    pub fn new(code: &str) -> Self {
        Self {
            code: code.to_string(),
            channel: "stable".to_string(),
            crate_type: "bin".to_string(),
            mode: "debug".to_string(),
            tests: false,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
pub struct ExecuteResponse {
    success: bool,
    stdout: String,
    stderr: String,
}
impl ExecuteResponse {
    pub fn result(&self) -> Result<&str, &str>{
        if self.success {
            Ok(&self.stdout)
        } else {
            Err(&self.stderr)
        }
    }
}

pub fn request_eval(code: &str) -> Result<ExecuteResponse, Error> {
    let playpen_url = env::var("PLAYPEN_URL").unwrap_or(String::new());

    let code_req = ExecuteRequest::new(code);
    let client = Client::new().unwrap();
    let mut res = client.post(&format!("{}/execute", playpen_url))?
        .json(&code_req)?
        .send()?;

    let content: ExecuteResponse = res.json()?;
    Ok(content)
}

#[test]
#[ignore]
fn test_it_executes_code() {
    env::set_var("PLAYPEN_URL", "https://play.rust-lang.org");

    let text = r#"fn main() { println!("hello word"); }"#;

    println!("{:?}", text);
    let result = match request_eval(&text) {
        Ok(res) => res,
        Err(err) => panic!("error: {:?}", err)
    };

    assert_eq!(result.success, true);
    assert_eq!(result.result(), Ok("hello word\n"))
}

#[test]
#[ignore]
fn test_it_executes_ivalid_code() {
    env::set_var("PLAYPEN_URL", "https://play.rust-lang.org");
    let text = r#"
        println!("hello word");
    }
"#;

    let result = match request_eval(&text) {
        Ok(res) => res,
        Err(err) => panic!("{:?}", err)
    };

    assert_eq!(result.success, false);
    assert_eq!(result.result(), Err(r#"   Compiling playground v0.0.1 (file:///playground)
error: unexpected close delimiter: `}`
 --> src/main.rs:3:5
  |
3 |     }
  |     ^

error: Could not compile `playground`.

To learn more, run the command again with --verbose.
"#))
}

<commit_msg>tests: Remove playpen default<commit_after>use std::env;

use std::io::Read;
use serde::{Serialize, Deserialize};
use reqwest::{Client, Error};
use reqwest::header::{Headers, ContentType};
use std::str;
use std::collections::HashMap;
use serde_json;

#[derive(Debug, Clone, Serialize)]
pub struct ExecuteRequest {
    channel: String,
    mode: String,
    #[serde(rename = "crateType")]
    crate_type: String,
    tests: bool,
    code: String,
}
impl ExecuteRequest {
    pub fn new(code: &str) -> Self {
        Self {
            code: code.to_string(),
            channel: "stable".to_string(),
            crate_type: "bin".to_string(),
            mode: "debug".to_string(),
            tests: false,
        }
    }
}

#[derive(Debug, Clone, Deserialize)]
pub struct ExecuteResponse {
    success: bool,
    stdout: String,
    stderr: String,
}
impl ExecuteResponse {
    pub fn result(&self) -> Result<&str, &str>{
        if self.success {
            Ok(&self.stdout)
        } else {
            Err(&self.stderr)
        }
    }
}

pub fn request_eval(code: &str) -> Result<ExecuteResponse, Error> {
    let playpen_url = env::var("PLAYPEN_URL").unwrap_or(String::new());

    let code_req = ExecuteRequest::new(code);
    let client = Client::new().unwrap();
    let mut res = client.post(&format!("{}/execute", playpen_url))?
        .json(&code_req)?
        .send()?;

    let content: ExecuteResponse = res.json()?;
    Ok(content)
}

#[test]
#[ignore]
fn test_it_executes_code() {
    let text = r#"fn main() { println!("hello word"); }"#;

    println!("{:?}", text);
    let result = match request_eval(&text) {
        Ok(res) => res,
        Err(err) => panic!("error: {:?}", err)
    };

    assert_eq!(result.success, true);
    assert_eq!(result.result(), Ok("hello word\n"))
}

#[test]
#[ignore]
fn test_it_executes_ivalid_code() {
    let text = r#"
        println!("hello word");
    }
"#;

    let result = match request_eval(&text) {
        Ok(res) => res,
        Err(err) => panic!("{:?}", err)
    };

    assert_eq!(result.success, false);
    assert_eq!(result.result(), Err(r#"   Compiling playground v0.0.1 (file:///playground)
error: unexpected close delimiter: `}`
 --> src/main.rs:3:5
  |
3 |     }
  |     ^

error: Could not compile `playground`.

To learn more, run the command again with --verbose.
"#))
}

<|endoftext|>


<commit_before>//! Encoding of portable pixmap Images

pub use self::encoder::PPMEncoder as PPMEncoder;
pub use self::decoder::PPMDecoder as PPMDecoder;

mod encoder;
mod decoder;

#[cfg(test)]
mod test {
    use color::ColorType;
    use image::{ImageDecoder, DecodingResult};

    #[test]
    fn test_roundtrip_ppm() {
        // 3x3 image that tries all the 0/255 RGB combinations
        let buf: [u8; 27] = [
              0,   0,   0,
              0,   0, 255,
              0, 255,   0,
              0, 255, 255,
            255,   0,   0,
            255,   0, 255,
            255, 255,   0,
            255, 255, 255,
            255, 255, 255,
        ];

        let mut stream = Vec::<u8>::new();
        {
            let mut encoder = super::PPMEncoder::new(&mut stream);
            match encoder.encode(&buf, 3, 3, ColorType::RGB(8)) {
                Ok(_) => {},
                Err(_) => panic!("PPM encoder failed"),
            };
        }

        let mut decoder = match super::PPMDecoder::new(&stream[..]) {
            Ok(img) => img,
            Err(e) => panic!("PPM decoder failed with {}", e),
        };
        match decoder.read_image() {
            Ok(DecodingResult::U8(vec)) => {
                assert_eq!(&buf[..], &vec[..]);
            },
            r => {
                panic!("PPM: Got a strange image result {:?}", r);
            }
        }
    }

    #[test]
    fn test_roundtrip_ppm_16bit() {
        // 3x3 image that tries all the 0/65535 RGB combinations plus a few more values
        // that check for big-endian conversion correctness
        let buf: [u16; 27] = [
                0,     0,     0,
                0,     0, 65535,
                0, 65535,     0,
                0, 65535, 65535,
            65535,     0,     0,
            65535,     0, 65535,
            65535, 65535,     0,
            65535, 65535, 65535,
             1000,  2000,  3000,
        ];
        let mut bytebuf = [0 as u8; 54];
        for (o, i) in bytebuf.chunks_mut(2).zip(buf.iter()) {
          o[0] = (i >> 8) as u8;
          o[1] = (i & 0xff) as u8;
        }

        let mut stream = Vec::<u8>::new();
        {
            let mut encoder = super::PPMEncoder::new(&mut stream);
            match encoder.encode(&bytebuf, 3, 3, ColorType::RGB(16)) {
                Ok(_) => {},
                Err(_) => panic!("PPM encoder failed"),
            };
        }

        let mut decoder = match super::PPMDecoder::new(&stream[..]) {
            Ok(img) => img,
            Err(e) => panic!("PPM decoder failed with {}", e),
        };
        match decoder.read_image() {
            Ok(DecodingResult::U16(vec)) => {
                assert_eq!(&buf[..], &vec[..]);
            },
            r => {
                panic!("PPM: Got a strange image result {:?}", r);
            }
        }
    }
}
<commit_msg>Add test for PPM comment handling<commit_after>//! Encoding of portable pixmap Images

pub use self::encoder::PPMEncoder as PPMEncoder;
pub use self::decoder::PPMDecoder as PPMDecoder;

mod encoder;
mod decoder;

#[cfg(test)]
mod test {
    use color::ColorType;
    use image::{ImageDecoder, DecodingResult};

    #[test]
    fn test_roundtrip_ppm() {
        // 3x3 image that tries all the 0/255 RGB combinations
        let buf: [u8; 27] = [
              0,   0,   0,
              0,   0, 255,
              0, 255,   0,
              0, 255, 255,
            255,   0,   0,
            255,   0, 255,
            255, 255,   0,
            255, 255, 255,
            255, 255, 255,
        ];

        let mut stream = Vec::<u8>::new();
        {
            let mut encoder = super::PPMEncoder::new(&mut stream);
            match encoder.encode(&buf, 3, 3, ColorType::RGB(8)) {
                Ok(_) => {},
                Err(_) => panic!("PPM encoder failed"),
            };
        }

        let mut decoder = match super::PPMDecoder::new(&stream[..]) {
            Ok(img) => img,
            Err(e) => panic!("PPM decoder failed with {}", e),
        };
        match decoder.read_image() {
            Ok(DecodingResult::U8(vec)) => {
                assert_eq!(&buf[..], &vec[..]);
            },
            r => {
                panic!("PPM: Got a strange image result {:?}", r);
            }
        }
    }

    #[test]
    fn test_roundtrip_ppm_16bit() {
        // 3x3 image that tries all the 0/65535 RGB combinations plus a few more values
        // that check for big-endian conversion correctness
        let buf: [u16; 27] = [
                0,     0,     0,
                0,     0, 65535,
                0, 65535,     0,
                0, 65535, 65535,
            65535,     0,     0,
            65535,     0, 65535,
            65535, 65535,     0,
            65535, 65535, 65535,
             1000,  2000,  3000,
        ];
        let mut bytebuf = [0 as u8; 54];
        for (o, i) in bytebuf.chunks_mut(2).zip(buf.iter()) {
          o[0] = (i >> 8) as u8;
          o[1] = (i & 0xff) as u8;
        }

        let mut stream = Vec::<u8>::new();
        {
            let mut encoder = super::PPMEncoder::new(&mut stream);
            match encoder.encode(&bytebuf, 3, 3, ColorType::RGB(16)) {
                Ok(_) => {},
                Err(_) => panic!("PPM encoder failed"),
            };
        }

        let mut decoder = match super::PPMDecoder::new(&stream[..]) {
            Ok(img) => img,
            Err(e) => panic!("PPM decoder failed with {}", e),
        };
        match decoder.read_image() {
            Ok(DecodingResult::U16(vec)) => {
                assert_eq!(&buf[..], &vec[..]);
            },
            r => {
                panic!("PPM: Got a strange image result {:?}", r);
            }
        }
    }

    #[test]
    fn test_ppm_comment() {
        // the following is a 1x1 white dot:
        let buf = "P3\n# asdf\n1 1\n255 255 255 255".as_bytes();

        let img = super::PPMDecoder::new(&buf[..]);
        assert_eq!(img.is_ok(), true);

        let img = img.unwrap().read_image();
        assert_eq!(img.is_ok(), true);
    }
}
<|endoftext|>


<commit_before>//! Contains types related to 2D image data.

use std::ops;
use std::mem;
use std::slice;
use gl;

/// A trait for types that that contain 2D image data, which can
/// be uploaded to a texture using a [`Texture2dBuilder`]
/// (../context/texture_context/struct.Texture2dBuilder)
/// or using the [`gl.image_2d`]
/// (../context/texture_context/trait.ContextTextureExt.html#method.image_2d)
/// method.
///
/// See [`Pixels`](struct.Pixels.html) for a simple `Image2d`.
pub trait Image2d {
    /// Get the width of the image, in texels.
    fn width(&self) -> usize;

    /// Get the height of the image, in texels.
    fn height(&self) -> usize;

    /// Get the format of the image data that is returned by the
    /// [`texel_bytes`](trait.Image2d.html#tymethod.texel_bytes) method.
    fn format(&self) -> ImageFormat;

    /// Get the raw texel data of the image data, as a `u8` slice.
    fn texel_bytes(&self) -> &[u8];
}

/// A single OpenGL color value, with `u8` components laid out
/// as a C struct in RGBA order. This type is the simplest implementation
/// of [`Image2d`](trait.Image2d.html), which allows it to be uploaded
/// to a texture.
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct Pixel {
    /// The red component.
    pub r: u8,

    /// The green component.
    pub g: u8,

    /// The blue component.
    pub b: u8,

    /// The alpha component.
    pub a: u8
}

impl Pixel {
    /// Create an RGBA color, provided each color component.
    ///
    /// # Examples
    /// ```
    /// let pixel = glitter::Pixel::r_g_b_a(0xAA, 0xBB, 0xCC, 0xDD);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xDD);
    /// ```
    pub fn r_g_b_a(r: u8, g: u8, b: u8, a: u8) -> Self {
        Pixel { r: r, g: g, b: b, a: a }
    }

    /// Create an RGBA color, provided the RGB components and using
    /// `0xFF` as the A value.
    ///
    /// # Examples
    /// ```
    /// let pixel = glitter::Pixel::r_g_b(0xAA, 0xBB, 0xCC);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xFF);
    /// ```
    pub fn r_g_b(r: u8, g: u8, b: u8) -> Self {
        Pixel::r_g_b_a(r, g, b, 0xFF)
    }

    /// Create an RGBA color, provided the RGB components as a packed
    /// `u32` value. The `u32` value will be read as `0x00RRGGBB`.
    ///
    /// # Examples
    /// ```
    /// // The `0x55` component gets discared
    /// let pixel = glitter::Pixel::rgb(0x55AABBCC);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xFF);
    /// ```
    pub fn rgb(rgb: u32) -> Self {
        Pixel::rgb_a(rgb, 0xFF)
    }

    /// Create an RGBA color value, provided the components as a packed
    /// `u32` value. The `u32` value will be read as `0xAARRGGBB`.
    ///
    /// # Examples
    /// ```
    /// let pixel = glitter::Pixel::argb(0xDDAABBCC);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xDD);
    /// ```
    pub fn argb(argb: u32) -> Self {
        let a = (argb & 0xFF000000) >> 24;
        Pixel::rgb_a(argb, a as u8)
    }

    /// Create an RGBA color, provided the RGB components as a packed
    /// `u32` value. The `u32` value will be read as `0xRRGGBBAA`.
    ///
    /// # Examples
    /// ```
    /// let pixel = glitter::Pixel::rgba(0xAABBCCDD);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xDD);
    /// ```
    pub fn rgba(rgba: u32) -> Self {
        let r = (rgba & 0xFF000000) >> 24;
        let g = (rgba & 0x00FF0000) >> 16;
        let b = (rgba & 0x0000FF00) >> 8;
        let a =  rgba & 0x000000FF;
        Pixel::r_g_b_a(r as u8, g as u8, b as u8, a as u8)
    }

    /// Create an RGBA color, provided the RGB components as a packed
    /// `u32` value, and a separate A component. The `u32` value
    /// will be read as `0x00RRGGBB`.
    ///
    /// # Examples
    /// ```
    /// // The `0x55` component gets discarded
    /// let pixel = glitter::Pixel::rgb_a(0x55AABBCC, 0xDD);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xDD);
    /// ```
    pub fn rgb_a(rgb: u32, a: u8) -> Self {
        let r = (rgb & 0xFF0000) >> 16;
        let g = (rgb & 0x00FF00) >> 8;
        let b =  rgb & 0x0000FF;
        Pixel::r_g_b_a(r as u8, g as u8, b as u8, a)
    }
}

/// A (heap-allocated) 2D image composed of a list of pixels.
///
/// # Example
///
/// ```
/// // Draw a red circle with a 50px radius in a 100px * 100px black image.
/// extern crate glitter;
///
/// let width = 100;
/// let height = 100;
/// let radius = 50;
///
// let (center_x, center_y) = (width as f32/2.0, height as f32/2.0);
//
// let mut pixels = glitter::Pixels::new(width, height);
// for x in 0..width {
//     for y in 0..height {
//         let dx = center_x - x as f32;
//         let dy = center_y - y as f32;
//         let distance = (dx*dx + dy*dy).sqrt();
//
//         let color = if distance < radius {
//             // The point is within the circle, so it should be red
//             glitter::Pixel::rgb(0xFF0000)
//         }
//         else {
//             // The point is outside the circle, so it should be black
//             glitter::Pixel::rgb(0x000000)
//         };
//         pixels[y][x] = color;
//     }
// }
// ```
pub struct Pixels {
    width: usize,
    height: usize,
    pixels: Vec<Pixel>
}

impl Pixels {
    /// Create a new image with the desired width and height.
    pub fn new(width: usize, height: usize) -> Self {
        Pixels {
            width: width,
            height: height,
            pixels: vec![Pixel::rgb(0x0000FF); width * height]
        }
    }
}

impl ops::Index<usize> for Pixels {
    type Output = [Pixel];

    fn index(&self, row: usize) -> &[Pixel] {
        debug_assert!(row < self.height);

        &self.pixels[(row*self.width)..((row+1)*self.width)]
    }
}

impl ops::IndexMut<usize> for Pixels {
    fn index_mut(&mut self, row: usize) -> &mut [Pixel] {
        debug_assert!(row < self.height);

        &mut self.pixels[(row*self.width)..((row+1)*self.width)]
    }
}

impl Image2d for Pixels {
    fn width(&self) -> usize {
        self.width
    }

    fn height(&self) -> usize {
        self.height
    }

    fn format(&self) -> ImageFormat {
        ImageFormat::rgba8()
    }

    fn texel_bytes(&self) -> &[u8] {
        let len = self.pixels.len() * mem::size_of::<Pixel>();
        unsafe {
            slice::from_raw_parts(mem::transmute(&self.pixels[0]), len)
        }
    }
}

gl_enum! {
    /// The data types for a texel.
    pub gl_enum TexelType {
        /// Each texel is 4 * 8 bits.
        pub const UnsignedByte as UNSIGNED_BYTE_TEXEL =
            gl::UNSIGNED_BYTE,

        /// Each texel is 16 bits, with 5-bit/6-bit/5-bit components.
        pub const UnsignedShort565 as UNSIGNED_SHORT_5_6_5 =
            gl::UNSIGNED_SHORT_5_6_5,

        /// Each texel is 16 bits, with 4, 4-bit components.
        pub const UnsignedShort4444 as UNSIGNED_SHORT_4_4_4_4 =
            gl::UNSIGNED_SHORT_4_4_4_4,

        /// Each texel is 16 bits, with 5-bit/5-bit/5-bit/1-bit components.
        pub const UnsignedShort5551 as UNSIGNED_SHORT_5_5_5_1 =
            gl::UNSIGNED_SHORT_5_5_5_1
    }
}

gl_enum! {
    /// The different texel formats.
    pub gl_enum TexelFormat {
        /// A texel contains only an alpha component.
        pub const Alpha as ALPHA = gl::ALPHA,

        /// A texel contains red, green, and blue components.
        pub const RGB as RGB = gl::RGB,

        /// A texel contains red, green, blue, and alpha components.
        pub const RGBA as RGBA = gl::RGBA
    }
}

gl_enum! {
    /// The various image formats of a renderbuffer.
    pub gl_enum RenderbufferFormat {
        /// The red, green, blue, and alpha channels are all stored with 4 bits.
        pub const RGBA4 as RGBA4 = gl::RGBA4,

        /// The red, green, and blue channels are stored with 5 bits, 6 bits,
        /// and 5 bits, respectively.
        pub const RGB565 as RGB565 = gl::RGB565,

        /// The red, green, and blue channels are stored with 5 bits, and the
        /// alpha channel is stored with 1 bit.
        pub const RGB5A1 as RGB5_A1 = gl::RGB5_A1,

        /// The renderbuffer stores a 16-bit depth component.
        pub const DepthComponent16 as DEPTH_COMPONENT16 = gl::DEPTH_COMPONENT16,

        /// The renderbuffer stores an 8-bit stencil component.
        pub const StencilIndex8 as STENCIL_INDEX8 = gl::STENCIL_INDEX8
    }
}

/// A type that contains the texel type and format that make up a 2D image.
#[derive(Debug, Clone, Copy)]
pub struct ImageFormat {
    /// The texel type of the image.
    pub texel_type: TexelType,

        /// The texel format of the image.
    pub texel_format: TexelFormat
}

impl ImageFormat {
    /// Returns the RGBA image format with 8 bits per component.
    pub fn rgba8() -> Self {
        ImageFormat {
            texel_type: TexelType::UnsignedByte,
            texel_format: TexelFormat::RGBA
        }
    }
}
<commit_msg>Use `Pixel::default()` for initializing `Pixels`<commit_after>//! Contains types related to 2D image data.

use std::ops;
use std::mem;
use std::slice;
use gl;

/// A trait for types that that contain 2D image data, which can
/// be uploaded to a texture using a [`Texture2dBuilder`]
/// (../context/texture_context/struct.Texture2dBuilder)
/// or using the [`gl.image_2d`]
/// (../context/texture_context/trait.ContextTextureExt.html#method.image_2d)
/// method.
///
/// See [`Pixels`](struct.Pixels.html) for a simple `Image2d`.
pub trait Image2d {
    /// Get the width of the image, in texels.
    fn width(&self) -> usize;

    /// Get the height of the image, in texels.
    fn height(&self) -> usize;

    /// Get the format of the image data that is returned by the
    /// [`texel_bytes`](trait.Image2d.html#tymethod.texel_bytes) method.
    fn format(&self) -> ImageFormat;

    /// Get the raw texel data of the image data, as a `u8` slice.
    fn texel_bytes(&self) -> &[u8];
}

/// A single OpenGL color value, with `u8` components laid out
/// as a C struct in RGBA order. This type is the simplest implementation
/// of [`Image2d`](trait.Image2d.html), which allows it to be uploaded
/// to a texture.
#[derive(Debug, Clone, Copy, Default)]
#[repr(C)]
pub struct Pixel {
    /// The red component.
    pub r: u8,

    /// The green component.
    pub g: u8,

    /// The blue component.
    pub b: u8,

    /// The alpha component.
    pub a: u8
}

impl Pixel {
    /// Create an RGBA color, provided each color component.
    ///
    /// # Examples
    /// ```
    /// let pixel = glitter::Pixel::r_g_b_a(0xAA, 0xBB, 0xCC, 0xDD);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xDD);
    /// ```
    pub fn r_g_b_a(r: u8, g: u8, b: u8, a: u8) -> Self {
        Pixel { r: r, g: g, b: b, a: a }
    }

    /// Create an RGBA color, provided the RGB components and using
    /// `0xFF` as the A value.
    ///
    /// # Examples
    /// ```
    /// let pixel = glitter::Pixel::r_g_b(0xAA, 0xBB, 0xCC);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xFF);
    /// ```
    pub fn r_g_b(r: u8, g: u8, b: u8) -> Self {
        Pixel::r_g_b_a(r, g, b, 0xFF)
    }

    /// Create an RGBA color, provided the RGB components as a packed
    /// `u32` value. The `u32` value will be read as `0x00RRGGBB`.
    ///
    /// # Examples
    /// ```
    /// // The `0x55` component gets discared
    /// let pixel = glitter::Pixel::rgb(0x55AABBCC);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xFF);
    /// ```
    pub fn rgb(rgb: u32) -> Self {
        Pixel::rgb_a(rgb, 0xFF)
    }

    /// Create an RGBA color value, provided the components as a packed
    /// `u32` value. The `u32` value will be read as `0xAARRGGBB`.
    ///
    /// # Examples
    /// ```
    /// let pixel = glitter::Pixel::argb(0xDDAABBCC);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xDD);
    /// ```
    pub fn argb(argb: u32) -> Self {
        let a = (argb & 0xFF000000) >> 24;
        Pixel::rgb_a(argb, a as u8)
    }

    /// Create an RGBA color, provided the RGB components as a packed
    /// `u32` value. The `u32` value will be read as `0xRRGGBBAA`.
    ///
    /// # Examples
    /// ```
    /// let pixel = glitter::Pixel::rgba(0xAABBCCDD);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xDD);
    /// ```
    pub fn rgba(rgba: u32) -> Self {
        let r = (rgba & 0xFF000000) >> 24;
        let g = (rgba & 0x00FF0000) >> 16;
        let b = (rgba & 0x0000FF00) >> 8;
        let a =  rgba & 0x000000FF;
        Pixel::r_g_b_a(r as u8, g as u8, b as u8, a as u8)
    }

    /// Create an RGBA color, provided the RGB components as a packed
    /// `u32` value, and a separate A component. The `u32` value
    /// will be read as `0x00RRGGBB`.
    ///
    /// # Examples
    /// ```
    /// // The `0x55` component gets discarded
    /// let pixel = glitter::Pixel::rgb_a(0x55AABBCC, 0xDD);
    /// assert_eq!(pixel.r, 0xAA);
    /// assert_eq!(pixel.g, 0xBB);
    /// assert_eq!(pixel.b, 0xCC);
    /// assert_eq!(pixel.a, 0xDD);
    /// ```
    pub fn rgb_a(rgb: u32, a: u8) -> Self {
        let r = (rgb & 0xFF0000) >> 16;
        let g = (rgb & 0x00FF00) >> 8;
        let b =  rgb & 0x0000FF;
        Pixel::r_g_b_a(r as u8, g as u8, b as u8, a)
    }
}

/// A (heap-allocated) 2D image composed of a list of pixels.
///
/// # Example
///
/// ```
/// // Draw a red circle with a 50px radius in a 100px * 100px black image.
/// extern crate glitter;
///
/// let width = 100;
/// let height = 100;
/// let radius = 50;
///
// let (center_x, center_y) = (width as f32/2.0, height as f32/2.0);
//
// let mut pixels = glitter::Pixels::new(width, height);
// for x in 0..width {
//     for y in 0..height {
//         let dx = center_x - x as f32;
//         let dy = center_y - y as f32;
//         let distance = (dx*dx + dy*dy).sqrt();
//
//         let color = if distance < radius {
//             // The point is within the circle, so it should be red
//             glitter::Pixel::rgb(0xFF0000)
//         }
//         else {
//             // The point is outside the circle, so it should be black
//             glitter::Pixel::rgb(0x000000)
//         };
//         pixels[y][x] = color;
//     }
// }
// ```
pub struct Pixels {
    width: usize,
    height: usize,
    pixels: Vec<Pixel>
}

impl Pixels {
    /// Create a new image with the desired width and height.
    pub fn new(width: usize, height: usize) -> Self {
        Pixels {
            width: width,
            height: height,
            pixels: vec![Pixel::default(); width * height]
        }
    }
}

impl ops::Index<usize> for Pixels {
    type Output = [Pixel];

    fn index(&self, row: usize) -> &[Pixel] {
        debug_assert!(row < self.height);

        &self.pixels[(row*self.width)..((row+1)*self.width)]
    }
}

impl ops::IndexMut<usize> for Pixels {
    fn index_mut(&mut self, row: usize) -> &mut [Pixel] {
        debug_assert!(row < self.height);

        &mut self.pixels[(row*self.width)..((row+1)*self.width)]
    }
}

impl Image2d for Pixels {
    fn width(&self) -> usize {
        self.width
    }

    fn height(&self) -> usize {
        self.height
    }

    fn format(&self) -> ImageFormat {
        ImageFormat::rgba8()
    }

    fn texel_bytes(&self) -> &[u8] {
        let len = self.pixels.len() * mem::size_of::<Pixel>();
        unsafe {
            slice::from_raw_parts(mem::transmute(&self.pixels[0]), len)
        }
    }
}

gl_enum! {
    /// The data types for a texel.
    pub gl_enum TexelType {
        /// Each texel is 4 * 8 bits.
        pub const UnsignedByte as UNSIGNED_BYTE_TEXEL =
            gl::UNSIGNED_BYTE,

        /// Each texel is 16 bits, with 5-bit/6-bit/5-bit components.
        pub const UnsignedShort565 as UNSIGNED_SHORT_5_6_5 =
            gl::UNSIGNED_SHORT_5_6_5,

        /// Each texel is 16 bits, with 4, 4-bit components.
        pub const UnsignedShort4444 as UNSIGNED_SHORT_4_4_4_4 =
            gl::UNSIGNED_SHORT_4_4_4_4,

        /// Each texel is 16 bits, with 5-bit/5-bit/5-bit/1-bit components.
        pub const UnsignedShort5551 as UNSIGNED_SHORT_5_5_5_1 =
            gl::UNSIGNED_SHORT_5_5_5_1
    }
}

gl_enum! {
    /// The different texel formats.
    pub gl_enum TexelFormat {
        /// A texel contains only an alpha component.
        pub const Alpha as ALPHA = gl::ALPHA,

        /// A texel contains red, green, and blue components.
        pub const RGB as RGB = gl::RGB,

        /// A texel contains red, green, blue, and alpha components.
        pub const RGBA as RGBA = gl::RGBA
    }
}

gl_enum! {
    /// The various image formats of a renderbuffer.
    pub gl_enum RenderbufferFormat {
        /// The red, green, blue, and alpha channels are all stored with 4 bits.
        pub const RGBA4 as RGBA4 = gl::RGBA4,

        /// The red, green, and blue channels are stored with 5 bits, 6 bits,
        /// and 5 bits, respectively.
        pub const RGB565 as RGB565 = gl::RGB565,

        /// The red, green, and blue channels are stored with 5 bits, and the
        /// alpha channel is stored with 1 bit.
        pub const RGB5A1 as RGB5_A1 = gl::RGB5_A1,

        /// The renderbuffer stores a 16-bit depth component.
        pub const DepthComponent16 as DEPTH_COMPONENT16 = gl::DEPTH_COMPONENT16,

        /// The renderbuffer stores an 8-bit stencil component.
        pub const StencilIndex8 as STENCIL_INDEX8 = gl::STENCIL_INDEX8
    }
}

/// A type that contains the texel type and format that make up a 2D image.
#[derive(Debug, Clone, Copy)]
pub struct ImageFormat {
    /// The texel type of the image.
    pub texel_type: TexelType,

        /// The texel format of the image.
    pub texel_format: TexelFormat
}

impl ImageFormat {
    /// Returns the RGBA image format with 8 bits per component.
    pub fn rgba8() -> Self {
        ImageFormat {
            texel_type: TexelType::UnsignedByte,
            texel_format: TexelFormat::RGBA
        }
    }
}
<|endoftext|>