<commit_before>use nom::{IResult, alpha, digit, space, rest};
use regex::{Regex, RegexBuilder};
use chrono::{Date, UTC, Datelike};

use std::str;
use std::fmt;

#[derive(Debug)]
pub enum ParserError {
    SummaryParsing(String),
    FooterParsing(String),
    CommitMessageLength,
}

impl fmt::Display for ParserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParserError::SummaryParsing(ref line) => write!(f, "Could not parse commit summary: {}", line),
            ParserError::FooterParsing(ref line) => write!(f, "Could not parse commit footer: {}", line),
            ParserError::CommitMessageLength => write!(f, "Commit message length too small."),
        }
    }
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ParsedTag {
    pub name: String,
    pub date: Date<UTC>,
}

impl fmt::Display for ParsedTag {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{} ({}-{:02}-{:02})",
               self.name,
               self.date.year(),
               self.date.month(),
               self.date.day())
    }
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ParsedCommit {
    pub summary: SummaryElement,
    pub body: Vec<BodyElement>,
    pub footer: Vec<FooterElement>,
}

impl fmt::Display for ParsedCommit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.summary));
        for item in &self.body {
            match *item {
                BodyElement::List(ref vec) => {
                    for item in vec {
                        try!(write!(f, "\n    {}", item.text));
                    }
                }
                BodyElement::Paragraph(ref par) => {
                    for line in par.text.lines().map(|x| format!("    {}", x)).collect::<Vec<String>>() {
                        try!(write!(f, "\n{}", line));
                    }
                }
            }
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct SummaryElement {
    pub prefix: String,
    pub category: String,
    pub text: String,
    pub tags: Vec<String>,
}

impl fmt::Display for SummaryElement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "- [{}]{}", self.category, self.text)
    }
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub enum BodyElement {
    List(Vec<ListElement>),
    Paragraph(ParagraphElement),
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ListElement {
    pub category: String,
    pub text: String,
    pub tags: Vec<String>,
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ParagraphElement {
    pub text: String,
    pub tags: Vec<String>,
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct FooterElement {
    pub key: String,
    pub value: String,
}

lazy_static! {
    static ref RE_TAGS: Regex = Regex::new(r" :(.*?):").unwrap();
    static ref RE_FOOTER: Regex = RegexBuilder::new(r"^([\w-]+):\s(.*)$").multi_line(true).compile().unwrap();
    static ref RE_LIST: Regex = RegexBuilder::new(r"^-\s.*$(\n^\s+-\s.*)*").multi_line(true).compile().unwrap();
}

pub struct Parser;
impl Parser {
    /// Parses a single commit message and returns a changelog ready form
    pub fn parse_commit_message(&self, message: &str) -> Result<ParsedCommit, ParserError> {

        /// Parses for tags and returns them with the resulting string
        fn parse_and_consume_tags(i: &[u8]) -> (Vec<String>, String) {
            let string = str::from_utf8(i).unwrap_or("");
            let mut tags = vec![];
            for cap in RE_TAGS.captures_iter(string) {
                tags.extend(cap.at(1).unwrap_or("").split(',').map(|x| x.trim().to_owned()).collect::<Vec<String>>());
            }
            (tags, RE_TAGS.replace_all(string, ""))
        }

        named!(parse_category<&str>,
            chain!(
                tag!("[")? ~
                p_category: map_res!(
                    alt!(
                        tag!("Added") |
                        tag!("Changed") |
                        tag!("Fixed") |
                        tag!("Improved") |
                        tag!("Removed")
                    ),
                    str::from_utf8
                ) ~
                tag!("]")? ,
                || p_category
            )
        );

        named!(parse_list_category<&str>,
            chain!(
                many0!(space) ~
                tag!("- ") ~
                p_category: parse_category ,
                || p_category
            )
        );

        // Every block is split by two newlines
        let mut commit_parts = message.split("\n\n");

        // Parse the summary line
        let summary_line = try!(commit_parts.nth(0).ok_or(ParserError::CommitMessageLength)).trim();
        named!(parse_summary<SummaryElement>,
            chain!(
                p_prefix: separated_pair!(alpha, char!('-'), digit)? ~
                space? ~
                p_category: parse_category ~
                tag!("]")? ~
                p_tags_rest: map!(rest, parse_and_consume_tags),
            || SummaryElement {
                prefix: p_prefix.map_or("".to_owned(), |p| {
                    format!("{}-{}", str::from_utf8(p.0).unwrap_or(""), str::from_utf8(p.1).unwrap_or(""))
                }),
                category: p_category.to_owned(),
                tags: p_tags_rest.0.clone(),
                text: p_tags_rest.1.clone(),
            })
        );
        let parsed_summary = match parse_summary(summary_line.as_bytes()) {
            IResult::Done(_, parsed) => parsed,
            _ => return Err(ParserError::SummaryParsing(summary_line.to_owned())),
        };

        // Parse the body and the footer, the summary is already consumed
        let mut parsed_footer = vec![];
        let mut parsed_body = vec![];
        for part in commit_parts {
            // Parse footer
            if RE_FOOTER.is_match(part) {
                for cap in RE_FOOTER.captures_iter(part) {
                    parsed_footer.push(FooterElement {
                        key: try!(cap.at(1).ok_or(ParserError::FooterParsing(part.to_owned()))).to_owned(),
                        value: try!(cap.at(2).ok_or(ParserError::FooterParsing(part.to_owned()))).to_owned(),
                    });
                }
            } else if RE_LIST.is_match(part) {
                // Parse list items
                let mut list = vec![];
                for list_item in part.lines() {
                    let (parsed_tags, parsed_text) = parse_and_consume_tags(list_item.as_bytes());
                    let parsed_category = match parse_list_category(list_item.as_bytes()) {
                        IResult::Done(_, cat) => cat,
                        _ => "",
                    };
                    list.push(ListElement {
                        category: parsed_category.to_owned(),
                        text: parsed_text,
                        tags: parsed_tags,
                    });
                }
                parsed_body.push(BodyElement::List(list));
            } else {
                // Assume paragraph
                let (parsed_tags, parsed_text) = parse_and_consume_tags(part.as_bytes());
                parsed_body.push(BodyElement::Paragraph(ParagraphElement {
                    text: parsed_text.trim().to_owned(),
                    tags: parsed_tags,
                }));
            }
        }

        Ok(ParsedCommit {
            summary: parsed_summary,
            body: parsed_body,
            footer: parsed_footer,
        })
    }
}
<commit_msg>Improved list item parsing<commit_after>use nom::{IResult, alpha, digit, space, rest};
use regex::{Regex, RegexBuilder};
use chrono::{Date, UTC, Datelike};

use std::str;
use std::fmt;

#[derive(Debug)]
pub enum ParserError {
    SummaryParsing(String),
    FooterParsing(String),
    CommitMessageLength,
}

impl fmt::Display for ParserError {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            ParserError::SummaryParsing(ref line) => write!(f, "Could not parse commit summary: {}", line),
            ParserError::FooterParsing(ref line) => write!(f, "Could not parse commit footer: {}", line),
            ParserError::CommitMessageLength => write!(f, "Commit message length too small."),
        }
    }
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ParsedTag {
    pub name: String,
    pub date: Date<UTC>,
}

impl fmt::Display for ParsedTag {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f,
               "{} ({}-{:02}-{:02})",
               self.name,
               self.date.year(),
               self.date.month(),
               self.date.day())
    }
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ParsedCommit {
    pub summary: SummaryElement,
    pub body: Vec<BodyElement>,
    pub footer: Vec<FooterElement>,
}

impl fmt::Display for ParsedCommit {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        try!(write!(f, "{}", self.summary));
        for item in &self.body {
            match *item {
                BodyElement::List(ref vec) => {
                    for item in vec {
                        try!(write!(f, "\n    - "));
                        if !item.category.is_empty() {
                            try!(write!(f, "[{}]", item.category));
                        }
                        try!(write!(f, "{}", item.text));
                    }
                }
                BodyElement::Paragraph(ref par) => {
                    for line in par.text.lines().map(|x| format!("    {}", x)).collect::<Vec<String>>() {
                        try!(write!(f, "\n{}", line));
                    }
                }
            }
        }
        Ok(())
    }
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct SummaryElement {
    pub prefix: String,
    pub category: String,
    pub text: String,
    pub tags: Vec<String>,
}

impl fmt::Display for SummaryElement {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        write!(f, "- [{}]{}", self.category, self.text)
    }
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub enum BodyElement {
    List(Vec<ListElement>),
    Paragraph(ParagraphElement),
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ListElement {
    pub category: String,
    pub text: String,
    pub tags: Vec<String>,
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct ParagraphElement {
    pub text: String,
    pub tags: Vec<String>,
}

#[derive(Debug, Clone, PartialOrd, Ord, PartialEq, Eq)]
pub struct FooterElement {
    pub key: String,
    pub value: String,
}

lazy_static! {
    static ref RE_TAGS: Regex = Regex::new(r" :(.*?):").unwrap();
    static ref RE_FOOTER: Regex = RegexBuilder::new(r"^([\w-]+):\s(.*)$").multi_line(true).compile().unwrap();
    static ref RE_LIST: Regex = RegexBuilder::new(r"^-\s.*$(\n^\s+-\s.*)*").multi_line(true).compile().unwrap();
}

pub struct Parser;
impl Parser {
    /// Parses a single commit message and returns a changelog ready form
    pub fn parse_commit_message(&self, message: &str) -> Result<ParsedCommit, ParserError> {

        /// Parses for tags and returns them with the resulting string
        fn parse_and_consume_tags(i: &[u8]) -> (Vec<String>, String) {
            let string = str::from_utf8(i).unwrap_or("");
            let mut tags = vec![];
            for cap in RE_TAGS.captures_iter(string) {
                tags.extend(cap.at(1).unwrap_or("").split(',').map(|x| x.trim().to_owned()).collect::<Vec<String>>());
            }
            (tags, RE_TAGS.replace_all(string, ""))
        }

        named!(parse_category<&str>,
            chain!(
                tag!("[")? ~
                p_category: map_res!(
                    alt!(
                        tag!("Added") |
                        tag!("Changed") |
                        tag!("Fixed") |
                        tag!("Improved") |
                        tag!("Removed")
                    ),
                    str::from_utf8
                ) ~
                tag!("]")? ,
                || p_category
            )
        );

        named!(parse_list_item<ListElement>,
            chain!(
                many0!(space) ~
                tag!("- ") ~
                p_category: parse_category ~
                p_tags_rest: map!(rest, parse_and_consume_tags),
                || ListElement {
                    category: p_category.to_owned(),
                    tags: p_tags_rest.0.clone(),
                    text: p_tags_rest.1.clone(),
                }
            )
        );

        // Every block is split by two newlines
        let mut commit_parts = message.split("\n\n");

        // Parse the summary line
        let summary_line = try!(commit_parts.nth(0).ok_or(ParserError::CommitMessageLength)).trim();
        named!(parse_summary<SummaryElement>,
            chain!(
                p_prefix: separated_pair!(alpha, char!('-'), digit)? ~
                space? ~
                p_category: parse_category ~
                tag!("]")? ~
                p_tags_rest: map!(rest, parse_and_consume_tags),
            || SummaryElement {
                prefix: p_prefix.map_or("".to_owned(), |p| {
                    format!("{}-{}", str::from_utf8(p.0).unwrap_or(""), str::from_utf8(p.1).unwrap_or(""))
                }),
                category: p_category.to_owned(),
                tags: p_tags_rest.0.clone(),
                text: p_tags_rest.1.clone(),
            })
        );
        let parsed_summary = match parse_summary(summary_line.as_bytes()) {
            IResult::Done(_, parsed) => parsed,
            _ => return Err(ParserError::SummaryParsing(summary_line.to_owned())),
        };

        // Parse the body and the footer, the summary is already consumed
        let mut parsed_footer = vec![];
        let mut parsed_body = vec![];
        for part in commit_parts {
            // Parse footer
            if RE_FOOTER.is_match(part) {
                for cap in RE_FOOTER.captures_iter(part) {
                    parsed_footer.push(FooterElement {
                        key: try!(cap.at(1).ok_or(ParserError::FooterParsing(part.to_owned()))).to_owned(),
                        value: try!(cap.at(2).ok_or(ParserError::FooterParsing(part.to_owned()))).to_owned(),
                    });
                }
            } else if RE_LIST.is_match(part) {
                // Parse list items
                let mut list = vec![];
                for list_item in part.lines() {
                    if let IResult::Done(_, result) = parse_list_item(list_item.as_bytes()) {
                        list.push(result);
                    };
                }
                parsed_body.push(BodyElement::List(list));
            } else {
                // Assume paragraph
                let (parsed_tags, parsed_text) = parse_and_consume_tags(part.as_bytes());
                parsed_body.push(BodyElement::Paragraph(ParagraphElement {
                    text: parsed_text.trim().to_owned(),
                    tags: parsed_tags,
                }));
            }
        }

        Ok(ParsedCommit {
            summary: parsed_summary,
            body: parsed_body,
            footer: parsed_footer,
        })
    }
}
<|endoftext|>


<commit_before>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Phrases
//!
//! A phrase is a string of symbols; it can be input to a DFA or part of an expression in a regular language.
//!

use std::slice::*;
use std::iter::FromIterator;

///
/// A phrase iterator can be used to return the symbols in a phrase one at a time
///
pub trait PhraseIterator<'a, Symbol> {
    fn next_symbol(&mut self) -> Option<&'a Symbol>;
}

///
/// Phrases are sequences of symbols, matched in order
///
pub trait Phrase<'a, Symbol> {
    type PhraseIterator: PhraseIterator<'a, Symbol>;

    ///
    /// Retrieves an iterator that can be used to read the symbols for this phrase
    ///
    fn get_symbols(&'a self) -> Self::PhraseIterator;
}

impl<'a, Symbol: 'a> Phrase<'a, Symbol> for Vec<Symbol> {
    type PhraseIterator = Iter<'a, Symbol>;

    #[inline]
    fn get_symbols(&'a self) -> Self::PhraseIterator {
        self.iter()
    }
}

impl<'a, Symbol> PhraseIterator<'a, Symbol> for Iter<'a, Symbol> {
    #[inline]
    fn next_symbol(&mut self) -> Option<&'a Symbol> {
        self.next()
    }
}

/*
impl<'a, Symbol> Phrase<Symbol> for &'a [Symbol] {
    type PhraseIterator = Iter<'a, Symbol>;

    #[inline]
    fn get_symbols(self) -> Self::PhraseIterator {
        self.iter()
    }
}

impl<'a> Phrase<char> for &'a str {
    type PhraseIterator = StringPhraseIterator;

    #[inline]
    fn get_symbols(self) -> Self::PhraseIterator {
        StringPhraseIterator { index: 0, string: Vec::from_iter(self.chars()) }
    }
}

///
/// Phrase iterator that goes over a string
///
pub struct StringPhraseIterator {
    /// Where we've reached in the string
    index: usize,

    /// The string that this iterator will cover
    string: Vec<char>
}

impl PhraseIterator<char> for StringPhraseIterator {
    fn next_symbol(&mut self) -> Option<&char> {
        if self.index >= self.string.len() {
            None
        } else {
            let result = Some(&self.string[self.index]);
            self.index += 1;

            result
        }
    }
}
*/

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

    #[test]
    fn can_iterate_vector_phrase() {
        let some_phrase     = vec![1, 2, 3];
        let mut iterator    = some_phrase.get_symbols();

        assert!(Some(&1) == iterator.next_symbol());
        assert!(Some(&2) == iterator.next_symbol());
        assert!(Some(&3) == iterator.next_symbol());
        assert!(iterator.next_symbol() == None);
    }

    /*
    #[test]
    fn can_iterate_array_phrase() {
        let some_phrase     = [1, 2, 3];
        let mut iterator    = some_phrase.get_symbols();

        assert!(iterator.next_symbol() == Some(&1));
        assert!(iterator.next_symbol() == Some(&2));
        assert!(iterator.next_symbol() == Some(&3));
        assert!(iterator.next_symbol() == None);
    }

    #[test]
    fn can_iterate_string_phrase() {
        let some_phrase     = "ABC";
        let mut iterator    = some_phrase.get_symbols();

        assert!(iterator.next_symbol() == Some(&'A'));
        assert!(iterator.next_symbol() == Some(&'B'));
        assert!(iterator.next_symbol() == Some(&'C'));
        assert!(iterator.next_symbol() == None);
    }
    */
}
<commit_msg>Fix up arrays to work the new way<commit_after>//
//   Copyright 2016 Andrew Hunter
//
//   Licensed under the Apache License, Version 2.0 (the "License");
//   you may not use this file except in compliance with the License.
//   You may obtain a copy of the License at
//
//       http://www.apache.org/licenses/LICENSE-2.0
//
//   Unless required by applicable law or agreed to in writing, software
//   distributed under the License is distributed on an "AS IS" BASIS,
//   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
//   See the License for the specific language governing permissions and
//   limitations under the License.
//

//!
//! # Phrases
//!
//! A phrase is a string of symbols; it can be input to a DFA or part of an expression in a regular language.
//!

use std::slice::*;
use std::iter::FromIterator;

///
/// A phrase iterator can be used to return the symbols in a phrase one at a time
///
pub trait PhraseIterator<'a, Symbol> {
    fn next_symbol(&mut self) -> Option<&'a Symbol>;
}

///
/// Phrases are sequences of symbols, matched in order
///
pub trait Phrase<'a, Symbol> {
    type PhraseIterator: PhraseIterator<'a, Symbol>;

    ///
    /// Retrieves an iterator that can be used to read the symbols for this phrase
    ///
    fn get_symbols(&'a self) -> Self::PhraseIterator;
}

impl<'a, Symbol: 'a> Phrase<'a, Symbol> for Vec<Symbol> {
    type PhraseIterator = Iter<'a, Symbol>;

    #[inline]
    fn get_symbols(&'a self) -> Self::PhraseIterator {
        self.iter()
    }
}

impl<'a, Symbol> PhraseIterator<'a, Symbol> for Iter<'a, Symbol> {
    #[inline]
    fn next_symbol(&mut self) -> Option<&'a Symbol> {
        self.next()
    }
}

impl<'a, Symbol: 'a> Phrase<'a, Symbol> for [Symbol] {
    type PhraseIterator = Iter<'a, Symbol>;

    #[inline]
    fn get_symbols(&'a self) -> Self::PhraseIterator {
        self.iter()
    }
}

/*
impl<'a> Phrase<char> for &'a str {
    type PhraseIterator = StringPhraseIterator;

    #[inline]
    fn get_symbols(self) -> Self::PhraseIterator {
        StringPhraseIterator { index: 0, string: Vec::from_iter(self.chars()) }
    }
}

///
/// Phrase iterator that goes over a string
///
pub struct StringPhraseIterator {
    /// Where we've reached in the string
    index: usize,

    /// The string that this iterator will cover
    string: Vec<char>
}

impl PhraseIterator<char> for StringPhraseIterator {
    fn next_symbol(&mut self) -> Option<&char> {
        if self.index >= self.string.len() {
            None
        } else {
            let result = Some(&self.string[self.index]);
            self.index += 1;

            result
        }
    }
}
*/

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

    #[test]
    fn can_iterate_vector_phrase() {
        let some_phrase     = vec![1, 2, 3];
        let mut iterator    = some_phrase.get_symbols();

        assert!(Some(&1) == iterator.next_symbol());
        assert!(Some(&2) == iterator.next_symbol());
        assert!(Some(&3) == iterator.next_symbol());
        assert!(iterator.next_symbol() == None);
    }

    #[test]
    fn can_iterate_array_phrase() {
        let some_phrase     = [1, 2, 3];
        let mut iterator    = some_phrase.get_symbols();

        assert!(Some(&1) == iterator.next_symbol());
        assert!(Some(&2) == iterator.next_symbol());
        assert!(Some(&3) == iterator.next_symbol());
        assert!(iterator.next_symbol() == None);
    }

    /*
    #[test]
    fn can_iterate_string_phrase() {
        let some_phrase     = "ABC";
        let mut iterator    = some_phrase.get_symbols();

        assert!(iterator.next_symbol() == Some(&'A'));
        assert!(iterator.next_symbol() == Some(&'B'));
        assert!(iterator.next_symbol() == Some(&'C'));
        assert!(iterator.next_symbol() == None);
    }
    */
}
<|endoftext|>


<commit_before>use std::cmp;
use std::fmt;
use std::io;
use std::ops::Deref;
#[cfg(feature = "termcolor")]
use termcolor::{ColorSpec, WriteColor};

use Doc;

/// Trait representing the operations necessary to render a document
pub trait Render {
    type Error;

    fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;

    fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
        while !s.is_empty() {
            let count = self.write_str(s)?;
            s = &s[count..];
        }
        Ok(())
    }
}

/// Writes to something implementing `std::io::Write`
pub struct IoWrite<W>(pub W);

impl<W> Render for IoWrite<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.0.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.0.write_all(s.as_bytes())
    }
}

/// Writes to something implementing `std::fmt::Write`
pub struct FmtWrite<W>(pub W);

impl<W> Render for FmtWrite<W>
where
    W: fmt::Write,
{
    type Error = fmt::Error;

    fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
        self.write_str_all(s).map(|_| s.len())
    }

    fn write_str_all(&mut self, s: &str) -> fmt::Result {
        self.0.write_str(s)
    }
}

/// Trait representing the operations necessary to write an annotated document.
pub trait RenderAnnotated<A>: Render {
    fn push_annotation(&mut self, annotation: &A) -> Result<(), Self::Error>;
    fn pop_annotation(&mut self) -> Result<(), Self::Error>;
}

impl<A, W> RenderAnnotated<A> for IoWrite<W>
where
    W: io::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl<A, W> RenderAnnotated<A> for FmtWrite<W>
where
    W: fmt::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

#[cfg(feature = "termcolor")]
pub struct TermColored<W> {
    color_stack: Vec<ColorSpec>,
    writer: W,
}

#[cfg(feature = "termcolor")]
impl<W> TermColored<W> {
    pub fn new(writer: W) -> TermColored<W> {
        TermColored {
            color_stack: Vec::new(),
            writer,
        }
    }
}

#[cfg(feature = "termcolor")]
impl<W> Render for TermColored<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.writer.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.writer.write_all(s.as_bytes())
    }
}

#[cfg(feature = "termcolor")]
impl<W> RenderAnnotated<ColorSpec> for TermColored<W>
where
    W: WriteColor,
{
    fn push_annotation(&mut self, color: &ColorSpec) -> Result<(), Self::Error> {
        self.color_stack.push(color.clone());
        self.writer.set_color(color)
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        self.color_stack.pop();
        match self.color_stack.last() {
            Some(previous) => self.writer.set_color(previous),
            None => self.writer.reset(),
        }
    }
}

#[inline]
pub fn best<'a, W, B, A>(doc: &'a Doc<'a, B, A>, width: usize, out: &mut W) -> Result<(), W::Error>
where
    B: Deref<Target = Doc<'a, B, A>>,
    W: ?Sized + RenderAnnotated<A>,
{
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
    enum Mode {
        Break,
        Flat,
    }

    type Cmd<'a, B, A> = (usize, Mode, &'a Doc<'a, B, A>);

    fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        try!(out.write_str_all("\n"));
        write_spaces(ind, out)
    }

    fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        macro_rules! make_spaces {
            () => { "" };
            ($s: tt $($t: tt)*) => { concat!("          ", make_spaces!($($t)*)) };
        }

        const SPACES: &str = make_spaces!(,,,,,,,,,,);
        let mut inserted = 0;
        while inserted < spaces {
            let insert = cmp::min(SPACES.len(), spaces - inserted);
            inserted += try!(out.write_str(&SPACES[..insert]));
        }

        Ok(())
    }

    #[inline]
    fn fitting<'a, B, A>(
        next: Cmd<'a, B, A>,
        bcmds: &[Cmd<'a, B, A>],
        fcmds: &mut Vec<Cmd<'a, B, A>>,
        mut rem: isize,
    ) -> bool
    where
        B: Deref<Target = Doc<'a, B, A>>,
    {
        let mut bidx = bcmds.len();
        fcmds.clear(); // clear from previous calls from best
        fcmds.push(next);

        while rem >= 0 {
            match fcmds.pop() {
                None => {
                    if bidx == 0 {
                        // All commands have been processed
                        return true;
                    } else {
                        fcmds.push(bcmds[bidx - 1]);
                        bidx -= 1;
                    }
                }
                Some((ind, mode, doc)) => {
                    match *doc {
                        Doc::Nil => {}
                        Doc::Append(ref ldoc, ref rdoc) => {
                            fcmds.push((ind, mode, rdoc));
                            // Since appended documents often appear in sequence on the left side we
                            // gain a slight performance increase by batching these pushes (avoiding
                            // to push and directly pop `Append` documents)
                            let mut doc = ldoc;
                            while let Doc::Append(ref l, ref r) = **doc {
                                fcmds.push((ind, mode, r));
                                doc = l;
                            }
                            fcmds.push((ind, mode, doc));
                        }
                        Doc::Group(ref doc) => {
                            fcmds.push((ind, mode, doc));
                        }
                        Doc::Nest(off, ref doc) => {
                            fcmds.push((ind + off, mode, doc));
                        }
                        Doc::Space => match mode {
                            Mode::Flat => {
                                rem -= 1;
                            }
                            Mode::Break => {
                                return true;
                            }
                        },
                        Doc::Newline => return true,
                        Doc::Text(ref str) => {
                            rem -= str.len() as isize;
                        }
                        Doc::Annotated(_, ref doc) => fcmds.push((ind, mode, doc)),
                    }
                }
            }
        }

        false
    }

    let mut pos = 0usize;
    let mut bcmds = vec![(0usize, Mode::Break, doc)];
    let mut fcmds = vec![];
    let mut annotation_levels = vec![];

    while let Some((ind, mode, doc)) = bcmds.pop() {
        match *doc {
            Doc::Nil => {}
            Doc::Append(ref ldoc, ref rdoc) => {
                bcmds.push((ind, mode, rdoc));
                let mut doc = ldoc;
                while let Doc::Append(ref l, ref r) = **doc {
                    bcmds.push((ind, mode, r));
                    doc = l;
                }
                bcmds.push((ind, mode, doc));
            }
            Doc::Group(ref doc) => match mode {
                Mode::Flat => {
                    bcmds.push((ind, Mode::Flat, doc));
                }
                Mode::Break => {
                    let next = (ind, Mode::Flat, &**doc);
                    let rem = width as isize - pos as isize;
                    if fitting(next, &bcmds, &mut fcmds, rem) {
                        bcmds.push(next);
                    } else {
                        bcmds.push((ind, Mode::Break, doc));
                    }
                }
            },
            Doc::Nest(off, ref doc) => {
                bcmds.push((ind + off, mode, doc));
            }
            Doc::Space => match mode {
                Mode::Flat => {
                    try!(write_spaces(1, out));
                }
                Mode::Break => {
                    try!(write_newline(ind, out));
                    pos = ind;
                }
            },
            Doc::Newline => {
                try!(write_newline(ind, out));
                pos = ind;

                // Since this newline caused an early break we don't know if the remaining
                // documents fit the next line so recalculate if they fit
                fcmds.clear();
                let docs = bcmds.len()
                    - bcmds
                        .iter()
                        .rev()
                        .position(|t| t.1 == Mode::Break)
                        .unwrap_or_else(|| bcmds.len());
                fcmds.extend_from_slice(&bcmds[docs..]);
                if let Some(next) = fcmds.pop() {
                    let rem = width as isize - pos as isize;
                    if !fitting(next, &bcmds, &mut fcmds, rem) {
                        for &mut (_, ref mut mode, _) in &mut bcmds[docs..] {
                            *mode = Mode::Break;
                        }
                    }
                }
            }
            Doc::Text(ref s) => {
                try!(out.write_str_all(s));
                pos += s.len();
            }
            Doc::Annotated(ref ann, ref doc) => {
                try!(out.push_annotation(ann));
                annotation_levels.push(bcmds.len());
                bcmds.push((ind, mode, doc));
            }
        }

        if annotation_levels.last() == Some(&bcmds.len()) {
            annotation_levels.pop();
            try!(out.pop_annotation());
        }
    }

    Ok(())
}
<commit_msg>Cleanup some explicit suffixes<commit_after>use std::cmp;
use std::fmt;
use std::io;
use std::ops::Deref;
#[cfg(feature = "termcolor")]
use termcolor::{ColorSpec, WriteColor};

use Doc;

/// Trait representing the operations necessary to render a document
pub trait Render {
    type Error;

    fn write_str(&mut self, s: &str) -> Result<usize, Self::Error>;

    fn write_str_all(&mut self, mut s: &str) -> Result<(), Self::Error> {
        while !s.is_empty() {
            let count = self.write_str(s)?;
            s = &s[count..];
        }
        Ok(())
    }
}

/// Writes to something implementing `std::io::Write`
pub struct IoWrite<W>(pub W);

impl<W> Render for IoWrite<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.0.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.0.write_all(s.as_bytes())
    }
}

/// Writes to something implementing `std::fmt::Write`
pub struct FmtWrite<W>(pub W);

impl<W> Render for FmtWrite<W>
where
    W: fmt::Write,
{
    type Error = fmt::Error;

    fn write_str(&mut self, s: &str) -> Result<usize, fmt::Error> {
        self.write_str_all(s).map(|_| s.len())
    }

    fn write_str_all(&mut self, s: &str) -> fmt::Result {
        self.0.write_str(s)
    }
}

/// Trait representing the operations necessary to write an annotated document.
pub trait RenderAnnotated<A>: Render {
    fn push_annotation(&mut self, annotation: &A) -> Result<(), Self::Error>;
    fn pop_annotation(&mut self) -> Result<(), Self::Error>;
}

impl<A, W> RenderAnnotated<A> for IoWrite<W>
where
    W: io::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

impl<A, W> RenderAnnotated<A> for FmtWrite<W>
where
    W: fmt::Write,
{
    fn push_annotation(&mut self, _: &A) -> Result<(), Self::Error> {
        Ok(())
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        Ok(())
    }
}

#[cfg(feature = "termcolor")]
pub struct TermColored<W> {
    color_stack: Vec<ColorSpec>,
    writer: W,
}

#[cfg(feature = "termcolor")]
impl<W> TermColored<W> {
    pub fn new(writer: W) -> TermColored<W> {
        TermColored {
            color_stack: Vec::new(),
            writer,
        }
    }
}

#[cfg(feature = "termcolor")]
impl<W> Render for TermColored<W>
where
    W: io::Write,
{
    type Error = io::Error;

    fn write_str(&mut self, s: &str) -> io::Result<usize> {
        self.writer.write(s.as_bytes())
    }

    fn write_str_all(&mut self, s: &str) -> io::Result<()> {
        self.writer.write_all(s.as_bytes())
    }
}

#[cfg(feature = "termcolor")]
impl<W> RenderAnnotated<ColorSpec> for TermColored<W>
where
    W: WriteColor,
{
    fn push_annotation(&mut self, color: &ColorSpec) -> Result<(), Self::Error> {
        self.color_stack.push(color.clone());
        self.writer.set_color(color)
    }

    fn pop_annotation(&mut self) -> Result<(), Self::Error> {
        self.color_stack.pop();
        match self.color_stack.last() {
            Some(previous) => self.writer.set_color(previous),
            None => self.writer.reset(),
        }
    }
}

#[inline]
pub fn best<'a, W, B, A>(doc: &'a Doc<'a, B, A>, width: usize, out: &mut W) -> Result<(), W::Error>
where
    B: Deref<Target = Doc<'a, B, A>>,
    W: ?Sized + RenderAnnotated<A>,
{
    #[derive(Clone, Copy, Debug, Eq, Ord, PartialEq, PartialOrd)]
    enum Mode {
        Break,
        Flat,
    }

    type Cmd<'a, B, A> = (usize, Mode, &'a Doc<'a, B, A>);

    fn write_newline<W>(ind: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        try!(out.write_str_all("\n"));
        write_spaces(ind, out)
    }

    fn write_spaces<W>(spaces: usize, out: &mut W) -> Result<(), W::Error>
    where
        W: ?Sized + Render,
    {
        macro_rules! make_spaces {
            () => { "" };
            ($s: tt $($t: tt)*) => { concat!("          ", make_spaces!($($t)*)) };
        }

        const SPACES: &str = make_spaces!(,,,,,,,,,,);
        let mut inserted = 0;
        while inserted < spaces {
            let insert = cmp::min(SPACES.len(), spaces - inserted);
            inserted += try!(out.write_str(&SPACES[..insert]));
        }

        Ok(())
    }

    #[inline]
    fn fitting<'a, B, A>(
        next: Cmd<'a, B, A>,
        bcmds: &[Cmd<'a, B, A>],
        fcmds: &mut Vec<Cmd<'a, B, A>>,
        mut rem: isize,
    ) -> bool
    where
        B: Deref<Target = Doc<'a, B, A>>,
    {
        let mut bidx = bcmds.len();
        fcmds.clear(); // clear from previous calls from best
        fcmds.push(next);

        while rem >= 0 {
            match fcmds.pop() {
                None => {
                    if bidx == 0 {
                        // All commands have been processed
                        return true;
                    } else {
                        fcmds.push(bcmds[bidx - 1]);
                        bidx -= 1;
                    }
                }
                Some((ind, mode, doc)) => {
                    match *doc {
                        Doc::Nil => {}
                        Doc::Append(ref ldoc, ref rdoc) => {
                            fcmds.push((ind, mode, rdoc));
                            // Since appended documents often appear in sequence on the left side we
                            // gain a slight performance increase by batching these pushes (avoiding
                            // to push and directly pop `Append` documents)
                            let mut doc = ldoc;
                            while let Doc::Append(ref l, ref r) = **doc {
                                fcmds.push((ind, mode, r));
                                doc = l;
                            }
                            fcmds.push((ind, mode, doc));
                        }
                        Doc::Group(ref doc) => {
                            fcmds.push((ind, mode, doc));
                        }
                        Doc::Nest(off, ref doc) => {
                            fcmds.push((ind + off, mode, doc));
                        }
                        Doc::Space => match mode {
                            Mode::Flat => {
                                rem -= 1;
                            }
                            Mode::Break => {
                                return true;
                            }
                        },
                        Doc::Newline => return true,
                        Doc::Text(ref str) => {
                            rem -= str.len() as isize;
                        }
                        Doc::Annotated(_, ref doc) => fcmds.push((ind, mode, doc)),
                    }
                }
            }
        }

        false
    }

    let mut pos = 0;
    let mut bcmds = vec![(0, Mode::Break, doc)];
    let mut fcmds = vec![];
    let mut annotation_levels = vec![];

    while let Some((ind, mode, doc)) = bcmds.pop() {
        match *doc {
            Doc::Nil => {}
            Doc::Append(ref ldoc, ref rdoc) => {
                bcmds.push((ind, mode, rdoc));
                let mut doc = ldoc;
                while let Doc::Append(ref l, ref r) = **doc {
                    bcmds.push((ind, mode, r));
                    doc = l;
                }
                bcmds.push((ind, mode, doc));
            }
            Doc::Group(ref doc) => match mode {
                Mode::Flat => {
                    bcmds.push((ind, Mode::Flat, doc));
                }
                Mode::Break => {
                    let next = (ind, Mode::Flat, &**doc);
                    let rem = width as isize - pos as isize;
                    if fitting(next, &bcmds, &mut fcmds, rem) {
                        bcmds.push(next);
                    } else {
                        bcmds.push((ind, Mode::Break, doc));
                    }
                }
            },
            Doc::Nest(off, ref doc) => {
                bcmds.push((ind + off, mode, doc));
            }
            Doc::Space => match mode {
                Mode::Flat => {
                    try!(write_spaces(1, out));
                }
                Mode::Break => {
                    try!(write_newline(ind, out));
                    pos = ind;
                }
            },
            Doc::Newline => {
                try!(write_newline(ind, out));
                pos = ind;

                // Since this newline caused an early break we don't know if the remaining
                // documents fit the next line so recalculate if they fit
                fcmds.clear();
                let docs = bcmds.len()
                    - bcmds
                        .iter()
                        .rev()
                        .position(|t| t.1 == Mode::Break)
                        .unwrap_or_else(|| bcmds.len());
                fcmds.extend_from_slice(&bcmds[docs..]);
                if let Some(next) = fcmds.pop() {
                    let rem = width as isize - pos as isize;
                    if !fitting(next, &bcmds, &mut fcmds, rem) {
                        for &mut (_, ref mut mode, _) in &mut bcmds[docs..] {
                            *mode = Mode::Break;
                        }
                    }
                }
            }
            Doc::Text(ref s) => {
                try!(out.write_str_all(s));
                pos += s.len();
            }
            Doc::Annotated(ref ann, ref doc) => {
                try!(out.push_annotation(ann));
                annotation_levels.push(bcmds.len());
                bcmds.push((ind, mode, doc));
            }
        }

        if annotation_levels.last() == Some(&bcmds.len()) {
            annotation_levels.pop();
            try!(out.pop_annotation());
        }
    }

    Ok(())
}
<|endoftext|>


<commit_before>// Needed because of https://github.com/Geal/nom/issues/345

use common::{self, word_match};
use nom::digit;
use num_traits::FromPrimitive;
use std::io;
use std::str::{self, FromStr};

#[derive(Debug)]
pub struct Word {
    pub word: String,
    pub score: u8,
}

impl Word {
    pub fn score(self: &Word) -> u8 {
        self.score
    }
}

#[derive(Debug)]
enum RawWord {
    Junk,
    Word(Word),
}

pub fn score(word: &str) -> u8 {
    let mut result = 0;
    for letter in word.chars() {
        result += match letter {
            // Scrabble scores from https://en.wikipedia.org/wiki/Scrabble_letter_distributions#English
            'a' | 'e' | 'i' | 'o' | 'u' | 'n' | 'r' | 't' | 'l' | 's' => 1,
            'd' | 'g' => 2,
            'b' | 'c' | 'm' | 'p' => 3,
            'f' | 'h' | 'v' | 'w' | 'y' => 4,
            'k' => 5,
            'j' | 'x' => 8,
            'q' | 'z' => 10,
            _ => 0,
        }
    }
    return result;
}

named!(get_word<&str, RawWord>,
    alt!(
        word_match => { |(begin, rest)| {
                let mut word = String::from(begin);
                word += rest;
                let sc = score(&word.to_lowercase());
                RawWord::Word(Word{word:word, score:sc})
            }
        } |
        take_s!( 1 )  => { |_| RawWord::Junk }
    )
);

named!(get_wotta<&str, RawWord>,
    map!(tuple!(
        tag_s!("["),
        digit,
        opt!(
            tuple!(
                tag_s!(":"),
                is_not_s!("]")
            )
        ),
        tag_s!("]"),
        opt!(tag_s!("\n"))
    ), |(_, raw_score, comment_opt, _, _)|{
        let word = String::from((comment_opt as Option<(&str, &str)>).unwrap().1);
        RawWord::Word(Word{word: word, score: u8::from_str(raw_score).unwrap()})
        })
);

fn word_filter(word: RawWord) -> Option<Word> {
    if let RawWord::Word(x) = word {
        Some(x)
    } else {
        None
    }
}

pub fn get_words_fn(filename: &str) -> Result<Vec<Word>, io::Error> {
    return common::get_words_core_fn(filename, get_word, word_filter);
}

pub fn get_words(buffer: &str) -> Result<Vec<Word>, io::Error> {
    return common::get_words_core(buffer, get_word, word_filter);
}

pub fn get_wottas_fn(filename: &str) -> Result<Vec<Word>, io::Error> {
    return common::get_words_core_fn(filename, get_wotta, word_filter);
}

pub fn get_wottas(buffer: &str) -> Result<Vec<Word>, io::Error> {
    return common::get_words_core(buffer, get_wotta, word_filter);
}

enum_from_primitive! {
    #[allow(non_camel_case_types)]
    #[derive(Debug, PartialEq)]
    enum Command {
        PUSH = 5,
        DISCARD = 6,
        ADD = 7,
        INPUT = 8,
        OUTPUT = 9,
        SUBTRACT = 10,
        SWAP = 11,
        DUP = 12,
        SKIP_AHEAD_ZERO = 13,
        SKIP_AHEAD_NONZERO = 14,
        SKIP_BACK_ZERO = 15,
        SKIP_BACK_NONZERO = 16,
        STOP = 17,
        NOP,
    }
}

fn action(act: u8) -> Command {
    return match Command::from_u8(act) {
        Some(val) => val,
        None => Command::NOP,
    };
}

pub fn run_beatnik(words: &[Word]) {
    let mut stack: Vec<u8> = Vec::new();
    let mut pc: usize = 0;
    loop {
        debug!("'{}' = {} ({:?})", words[pc].word, words[pc].score, action(words[pc].score));
        match action(words[pc].score) {
            Command::PUSH => {
                pc += 1;
                debug!("Pushing {} to stack", words[pc].score);
                stack.push(words[pc].score);
            }
            Command::DISCARD => {
                stack.pop().expect("stack value");
            }
            Command::ADD => {
                let x = stack.pop().expect("first value");
                let y = stack.pop().expect("second value");
                stack.push(x.wrapping_add(y));
            }
            Command::INPUT => {
                // FIXME: read from stdin
                stack.push(b'a');
            }
            Command::OUTPUT => print!("{}", stack.pop().expect("character on stack") as char),
            Command::SUBTRACT => {
                let x = stack.pop().expect("first value");
                let y = stack.pop().expect("second value");
                stack.push(y.wrapping_sub(x));
            }
            Command::SWAP => {
                let x = stack.pop().expect("first value");
                let y = stack.pop().expect("second value");
                stack.push(x);
                stack.push(y);
            }
            Command::DUP => {
                let x = stack.pop().expect("first value");
                stack.push(x);
                stack.push(x);
            }
            Command::SKIP_AHEAD_NONZERO => {
                let check = stack.pop().expect("check value");
                pc += 1;
                if check != 0 {
                    pc += words[pc].score as usize;
                }
            }
            Command::SKIP_AHEAD_ZERO => {
                let check = stack.pop().expect("check value");
                pc += 1;
                if check == 0 {
                    pc += words[pc].score as usize;
                }
            }
            Command::SKIP_BACK_ZERO => {
                let check = stack.pop().expect("check value");
                if check == 0 {
                    pc -= words[pc + 1].score as usize;
                }
            }
            Command::SKIP_BACK_NONZERO => {
                let check = stack.pop().expect("check value");
                if check != 0 {
                    pc -= words[pc + 1].score as usize;
                }
            }
            Command::STOP => break,
            Command::NOP => {}
        }
        pc += 1;
        if pc == words.len() {
            break;
        }
    }
    debug!("Stack: {:?}", stack);
}

pub fn output_wottasquare(words: Vec<Word>) {
    for word in words {
        println!("[{}:{:?}]", word.score, action(word.score));
    }
}

#[cfg(test)]
mod tests {
    use quickcheck::TestResult;
    use super::{get_words, get_wottas};

    #[test]
    fn test_apostrophe() {
        let ref word = get_words("she’s").unwrap()[0];
        assert_eq!(word.score, 7);
    }

    quickcheck! {
      fn word_test(xs: String) -> TestResult {
          return match get_words(&xs) {
              Ok(_) => TestResult::passed(),
              Err(err) => {
                  println!("Error: '{}'", xs);
                  TestResult::error(format!("{:?}", err))
              }
          }
      }

      fn wotta_test(xs: String) -> TestResult {
        if xs.len() == 0 {
            return TestResult::discard();
        }
        if xs.find("]").is_some() {
            return TestResult::discard();
        }

        return match get_wottas(&format!("[1:{}]", xs)) {
            Ok(_) => TestResult::passed(),
            Err(err) => {
                println!("Error: '{}'", xs);
                TestResult::error(format!("{:?}", err))
            }
        }
      }
  }
}
<commit_msg>get_words/wottas only used in tests<commit_after>// Needed because of https://github.com/Geal/nom/issues/345

use common::{self, word_match};
use nom::digit;
use num_traits::FromPrimitive;
use std::io;
use std::str::{self, FromStr};

#[derive(Debug)]
pub struct Word {
    pub word: String,
    pub score: u8,
}

impl Word {
    pub fn score(self: &Word) -> u8 {
        self.score
    }
}

#[derive(Debug)]
enum RawWord {
    Junk,
    Word(Word),
}

pub fn score(word: &str) -> u8 {
    let mut result = 0;
    for letter in word.chars() {
        result += match letter {
            // Scrabble scores from https://en.wikipedia.org/wiki/Scrabble_letter_distributions#English
            'a' | 'e' | 'i' | 'o' | 'u' | 'n' | 'r' | 't' | 'l' | 's' => 1,
            'd' | 'g' => 2,
            'b' | 'c' | 'm' | 'p' => 3,
            'f' | 'h' | 'v' | 'w' | 'y' => 4,
            'k' => 5,
            'j' | 'x' => 8,
            'q' | 'z' => 10,
            _ => 0,
        }
    }
    return result;
}

named!(get_word<&str, RawWord>,
    alt!(
        word_match => { |(begin, rest)| {
                let mut word = String::from(begin);
                word += rest;
                let sc = score(&word.to_lowercase());
                RawWord::Word(Word{word:word, score:sc})
            }
        } |
        take_s!( 1 )  => { |_| RawWord::Junk }
    )
);

named!(get_wotta<&str, RawWord>,
    map!(tuple!(
        tag_s!("["),
        digit,
        opt!(
            tuple!(
                tag_s!(":"),
                is_not_s!("]")
            )
        ),
        tag_s!("]"),
        opt!(tag_s!("\n"))
    ), |(_, raw_score, comment_opt, _, _)|{
        let word = String::from((comment_opt as Option<(&str, &str)>).unwrap().1);
        RawWord::Word(Word{word: word, score: u8::from_str(raw_score).unwrap()})
        })
);

fn word_filter(word: RawWord) -> Option<Word> {
    if let RawWord::Word(x) = word {
        Some(x)
    } else {
        None
    }
}

pub fn get_words_fn(filename: &str) -> Result<Vec<Word>, io::Error> {
    return common::get_words_core_fn(filename, get_word, word_filter);
}

#[cfg(test)]
pub fn get_words(buffer: &str) -> Result<Vec<Word>, io::Error> {
    return common::get_words_core(buffer, get_word, word_filter);
}

pub fn get_wottas_fn(filename: &str) -> Result<Vec<Word>, io::Error> {
    return common::get_words_core_fn(filename, get_wotta, word_filter);
}

#[cfg(test)]
pub fn get_wottas(buffer: &str) -> Result<Vec<Word>, io::Error> {
    return common::get_words_core(buffer, get_wotta, word_filter);
}

enum_from_primitive! {
    #[allow(non_camel_case_types)]
    #[derive(Debug, PartialEq)]
    enum Command {
        PUSH = 5,
        DISCARD = 6,
        ADD = 7,
        INPUT = 8,
        OUTPUT = 9,
        SUBTRACT = 10,
        SWAP = 11,
        DUP = 12,
        SKIP_AHEAD_ZERO = 13,
        SKIP_AHEAD_NONZERO = 14,
        SKIP_BACK_ZERO = 15,
        SKIP_BACK_NONZERO = 16,
        STOP = 17,
        NOP,
    }
}

fn action(act: u8) -> Command {
    return match Command::from_u8(act) {
        Some(val) => val,
        None => Command::NOP,
    };
}

pub fn run_beatnik(words: &[Word]) {
    let mut stack: Vec<u8> = Vec::new();
    let mut pc: usize = 0;
    loop {
        debug!("'{}' = {} ({:?})", words[pc].word, words[pc].score, action(words[pc].score));
        match action(words[pc].score) {
            Command::PUSH => {
                pc += 1;
                debug!("Pushing {} to stack", words[pc].score);
                stack.push(words[pc].score);
            }
            Command::DISCARD => {
                stack.pop().expect("stack value");
            }
            Command::ADD => {
                let x = stack.pop().expect("first value");
                let y = stack.pop().expect("second value");
                stack.push(x.wrapping_add(y));
            }
            Command::INPUT => {
                // FIXME: read from stdin
                stack.push(b'a');
            }
            Command::OUTPUT => print!("{}", stack.pop().expect("character on stack") as char),
            Command::SUBTRACT => {
                let x = stack.pop().expect("first value");
                let y = stack.pop().expect("second value");
                stack.push(y.wrapping_sub(x));
            }
            Command::SWAP => {
                let x = stack.pop().expect("first value");
                let y = stack.pop().expect("second value");
                stack.push(x);
                stack.push(y);
            }
            Command::DUP => {
                let x = stack.pop().expect("first value");
                stack.push(x);
                stack.push(x);
            }
            Command::SKIP_AHEAD_NONZERO => {
                let check = stack.pop().expect("check value");
                pc += 1;
                if check != 0 {
                    pc += words[pc].score as usize;
                }
            }
            Command::SKIP_AHEAD_ZERO => {
                let check = stack.pop().expect("check value");
                pc += 1;
                if check == 0 {
                    pc += words[pc].score as usize;
                }
            }
            Command::SKIP_BACK_ZERO => {
                let check = stack.pop().expect("check value");
                if check == 0 {
                    pc -= words[pc + 1].score as usize;
                }
            }
            Command::SKIP_BACK_NONZERO => {
                let check = stack.pop().expect("check value");
                if check != 0 {
                    pc -= words[pc + 1].score as usize;
                }
            }
            Command::STOP => break,
            Command::NOP => {}
        }
        pc += 1;
        if pc == words.len() {
            break;
        }
    }
    debug!("Stack: {:?}", stack);
}

pub fn output_wottasquare(words: Vec<Word>) {
    for word in words {
        println!("[{}:{:?}]", word.score, action(word.score));
    }
}

#[cfg(test)]
mod tests {
    use quickcheck::TestResult;
    use super::{get_words, get_wottas};

    #[test]
    fn test_apostrophe() {
        let ref word = get_words("she’s").unwrap()[0];
        assert_eq!(word.score, 7);
    }

    quickcheck! {
      fn word_test(xs: String) -> TestResult {
          return match get_words(&xs) {
              Ok(_) => TestResult::passed(),
              Err(err) => {
                  println!("Error: '{}'", xs);
                  TestResult::error(format!("{:?}", err))
              }
          }
      }

      fn wotta_test(xs: String) -> TestResult {
        if xs.len() == 0 {
            return TestResult::discard();
        }
        if xs.find("]").is_some() {
            return TestResult::discard();
        }

        return match get_wottas(&format!("[1:{}]", xs)) {
            Ok(_) => TestResult::passed(),
            Err(err) => {
                println!("Error: '{}'", xs);
                TestResult::error(format!("{:?}", err))
            }
        }
      }
  }
}
<|endoftext|>


<commit_before>//! Interfaces for searching for issues

// Third party
use url::form_urlencoded;

// Ours
use {Issue, Jira, Result, SearchOptions, SearchResults};

/// search interface
#[derive(Debug)]
pub struct Search {
    jira: Jira,
}

impl Search {
    pub fn new(jira: &Jira) -> Search {
        Search { jira: jira.clone() }
    }

    /// returns a single page of search results
    /// https://docs.atlassian.com/jira/REST/latest/#api/2/search
    pub fn list<J>(&self, jql: J, options: &SearchOptions) -> Result<SearchResults>
    where
        J: Into<String>,
    {
        let mut path = vec!["/search".to_owned()];
        let query_options = options.serialize().unwrap_or_default();
        let query = form_urlencoded::Serializer::new(query_options)
            .append_pair("jql", &jql.into())
            .finish();
        path.push(query);
        self.jira
            .get::<SearchResults>("api", path.join("?").as_ref())
    }

    /// runs a type why may be used to iterate over consecutive pages of results
    /// https://docs.atlassian.com/jira/REST/latest/#api/2/search
    pub fn iter<'a, J>(&self, jql: J, options: &'a SearchOptions) -> Result<Iter<'a>>
    where
        J: Into<String>,
    {
        Iter::new(jql, options, &self.jira)
    }
}

/// provides an iterator over multiple pages of search results
#[derive(Debug)]
pub struct Iter<'a> {
    jira: Jira,
    jql: String,
    results: SearchResults,
    search_options: &'a SearchOptions,
}

impl<'a> Iter<'a> {
    fn new<J>(jql: J, options: &'a SearchOptions, jira: &Jira) -> Result<Self>
    where
        J: Into<String>,
    {
        let query = jql.into();
        let results = jira.search().list(query.clone(), options)?;
        Ok(Iter {
            jira: jira.clone(),
            jql: query,
            results,
            search_options: options,
        })
    }

    fn more(&self) -> bool {
        (self.results.start_at + self.results.issues.len() as u64) < self.results.total
    }
}

impl<'a> Iterator for Iter<'a> {
    type Item = Issue;
    fn next(&mut self) -> Option<Issue> {
        self.results.issues.pop().or_else(|| {
            if self.more() {
                match self.jira.search().list(
                    self.jql.clone(),
                    &self.search_options
                        .as_builder()
                        .max_results(self.results.max_results)
                        .start_at(self.results.start_at + self.results.max_results)
                        .build(),
                ) {
                    Ok(new_results) => {
                        self.results = new_results;
                        self.results.issues.pop()
                    }
                    _ => None,
                }
            } else {
                None
            }
        })
    }
}
<commit_msg>fix some doc typos<commit_after>//! Interfaces for searching for issues

// Third party
use url::form_urlencoded;

// Ours
use {Issue, Jira, Result, SearchOptions, SearchResults};

/// Search interface
#[derive(Debug)]
pub struct Search {
    jira: Jira,
}

impl Search {
    pub fn new(jira: &Jira) -> Search {
        Search { jira: jira.clone() }
    }

    /// Returns a single page of search results
    ///
    /// See the [jira docs](https://docs.atlassian.com/jira/REST/latest/#api/2/search)
    /// for more information
    pub fn list<J>(&self, jql: J, options: &SearchOptions) -> Result<SearchResults>
    where
        J: Into<String>,
    {
        let mut path = vec!["/search".to_owned()];
        let query_options = options.serialize().unwrap_or_default();
        let query = form_urlencoded::Serializer::new(query_options)
            .append_pair("jql", &jql.into())
            .finish();
        path.push(query);
        self.jira
            .get::<SearchResults>("api", path.join("?").as_ref())
    }

    /// Return a type which may be used to iterate over consecutive pages of results
    ///
    /// See the [jira docs](https://docs.atlassian.com/jira/REST/latest/#api/2/search)
    /// for more information
    pub fn iter<'a, J>(&self, jql: J, options: &'a SearchOptions) -> Result<Iter<'a>>
    where
        J: Into<String>,
    {
        Iter::new(jql, options, &self.jira)
    }
}

/// provides an iterator over multiple pages of search results
#[derive(Debug)]
pub struct Iter<'a> {
    jira: Jira,
    jql: String,
    results: SearchResults,
    search_options: &'a SearchOptions,
}

impl<'a> Iter<'a> {
    fn new<J>(jql: J, options: &'a SearchOptions, jira: &Jira) -> Result<Self>
    where
        J: Into<String>,
    {
        let query = jql.into();
        let results = jira.search().list(query.clone(), options)?;
        Ok(Iter {
            jira: jira.clone(),
            jql: query,
            results,
            search_options: options,
        })
    }

    fn more(&self) -> bool {
        (self.results.start_at + self.results.issues.len() as u64) < self.results.total
    }
}

impl<'a> Iterator for Iter<'a> {
    type Item = Issue;
    fn next(&mut self) -> Option<Issue> {
        self.results.issues.pop().or_else(|| {
            if self.more() {
                match self.jira.search().list(
                    self.jql.clone(),
                    &self.search_options
                        .as_builder()
                        .max_results(self.results.max_results)
                        .start_at(self.results.start_at + self.results.max_results)
                        .build(),
                ) {
                    Ok(new_results) => {
                        self.results = new_results;
                        self.results.issues.pop()
                    }
                    _ => None,
                }
            } else {
                None
            }
        })
    }
}
<|endoftext|>


<commit_before>use std::iter::Iterator;
use std::collections::HashSet;
use std::marker::PhantomData;
use std::hash::{Hash, Hasher, SipHasher};

struct HashVisitor<T> {
    hash_set: HashSet<u64>,
    phantom: PhantomData<T>,
}

enum Visit {
    FirstTime,
    Revisit
}

impl<T> HashVisitor<T> where T: Hash {
    fn new() -> HashVisitor<T> {
        HashVisitor {
            hash_set: HashSet::new(),
            phantom: PhantomData
        }
    }

    fn visit(&mut self, item: &T) -> Visit {
        let mut hasher = SipHasher::new();
        item.hash(&mut hasher);
        let hash = hasher.finish();
        match self.hash_set.insert(hash) {
            true => Visit::FirstTime,
            false => Visit::Revisit
        }
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        *self == *state
    }
}

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: Self::State, goal: G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(&start) {
            return Some(vec![]);
        }

        let mut path = Vec::new();
        let mut visited = HashVisitor::new();
        let mut frontier = vec![self.expand(&start)];

        loop {
            let result = match frontier.last_mut() {
                None => {
                    return None
                },
                Some(&mut ref mut iter) => {
                    match iter.next() {
                        None => {
                            path.pop();
                            None
                        },
                        Some((action, state)) => {
                            if let Visit::Revisit = visited.visit(&state) {
                                continue;
                            }
                            path.push(action);
                            if goal.is_goal(&state) {
                                return Some(path);
                            }
                            Some(self.expand(&state))
                        }
                    }
                }
            };
            match result {
                None => {
                    frontier.pop();
                }
                Some(iter) => {
                    frontier.push(iter);
                }
            };
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::SearchSpace;

    #[test]
    pub fn test_dfs() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Dir::Left, 1), (Dir::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Dir::Left, 3), (Dir::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Dir::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(0, 0).unwrap(), vec![]);
        assert_eq!(ts.dfs(0, 1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(0, 2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(0, 3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(0, 4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(2, 2).unwrap(), vec![]);
        assert!(ts.dfs(2, 0).is_none());
        assert!(ts.dfs(5, 0).is_none());
    }
}
<commit_msg>renamed HashVisitor to Visited and changes API to match the one of HashSet<commit_after>use std::iter::Iterator;
use std::collections::HashSet;
use std::marker::PhantomData;
use std::hash::{Hash, Hasher, SipHasher};

struct Visited<T> {
    hash_set: HashSet<u64>,
    phantom: PhantomData<T>,
}

impl<T> Visited<T> where T: Hash {
    fn new() -> Visited<T> {
        Visited {
            hash_set: HashSet::new(),
            phantom: PhantomData,
        }
    }

    fn insert(&mut self, value: &T) -> bool {
        let mut hasher = SipHasher::new();
        value.hash(&mut hasher);
        self.hash_set.insert(hasher.finish())
    }
}

pub trait SearchGoal<T> {
    fn is_goal(&self, state: &T) -> bool;
}

impl<T> SearchGoal<T> for T where T: PartialEq {
    fn is_goal(&self, state: &T) -> bool {
        *self == *state
    }
}

pub trait SearchSpace {
    type State: Hash;
    type Action;
    type Iterator: Iterator<Item=(Self::Action, Self::State)>;

    fn expand(&self, state: &Self::State) -> Self::Iterator;

    fn dfs<G>(&self, start: Self::State, goal: G) -> Option<Vec<Self::Action>>
    where G: SearchGoal<Self::State> {
        if goal.is_goal(&start) {
            return Some(vec![]);
        }

        let mut path = Vec::new();
        let mut visited = Visited::new();
        let mut frontier = vec![self.expand(&start)];

        loop {
            let result = match frontier.last_mut() {
                None => {
                    return None
                },
                Some(&mut ref mut iter) => {
                    match iter.next() {
                        None => {
                            path.pop();
                            None
                        },
                        Some((action, state)) => {
                            if !visited.insert(&state) {
                                continue;
                            }
                            path.push(action);
                            if goal.is_goal(&state) {
                                return Some(path);
                            }
                            Some(self.expand(&state))
                        }
                    }
                }
            };
            match result {
                None => {
                    frontier.pop();
                }
                Some(iter) => {
                    frontier.push(iter);
                }
            };
        }
    }
}

#[cfg(test)]
pub mod tests {
    use std::vec::IntoIter;
    use super::SearchSpace;

    #[test]
    pub fn test_dfs() {
        struct TestSearch;

        #[derive(Debug, PartialEq)]
        enum Dir { Left, Right }

        impl SearchSpace for TestSearch {
            type State = i32;
            type Action = Dir;
            type Iterator = IntoIter<(Self::Action, Self::State)>;

            fn expand(&self, state: &Self::State) -> Self::Iterator {
                if *state == 0 {
                    vec![(Dir::Left, 1), (Dir::Right, 2)].into_iter()
                } else if *state == 1 {
                    vec![(Dir::Left, 3), (Dir::Right, 4)].into_iter()
                } else if *state == 2 {
                    vec![(Dir::Left, 2)].into_iter()
                } else {
                    vec![].into_iter()
                }
            }
        }

        let ts = TestSearch;

        assert_eq!(ts.dfs(0, 0).unwrap(), vec![]);
        assert_eq!(ts.dfs(0, 1).unwrap(), vec![Dir::Left]);
        assert_eq!(ts.dfs(0, 2).unwrap(), vec![Dir::Right]);
        assert_eq!(ts.dfs(0, 3).unwrap(), vec![Dir::Left, Dir::Left]);
        assert_eq!(ts.dfs(0, 4).unwrap(), vec![Dir::Left, Dir::Right]);
        assert_eq!(ts.dfs(2, 2).unwrap(), vec![]);
        assert!(ts.dfs(2, 0).is_none());
        assert!(ts.dfs(5, 0).is_none());
    }
}
<|endoftext|>


<commit_before>/*!
The search module is responsible for searching a single file and printing
matches.
*/

use std::cmp;
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

use grep::{Grep, Match};
use memchr::{memchr, memrchr};

use printer::Printer;

/// The default read size (capacity of input buffer).
const READ_SIZE: usize = 8 * (1<<10);

/// Error describes errors that can occur while searching.
#[derive(Debug)]
pub enum Error {
    /// Normal IO or Mmap errors suck. Include the path the originated them.
    Io {
        err: io::Error,
        path: PathBuf,
    }
}

impl Error {
    fn from_io<P: AsRef<Path>>(err: io::Error, path: P) -> Error {
        Error::Io { err: err, path: path.as_ref().to_path_buf() }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io { ref err, .. } => err.description(),
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match *self {
            Error::Io { ref err, .. } => Some(err),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io { ref err, ref path } => {
                write!(f, "{}: {}", path.display(), err)
            }
        }
    }
}

pub struct Searcher<'a, R, W: 'a> {
    inp: &'a mut InputBuffer,
    printer: &'a mut Printer<W>,
    grep: &'a Grep,
    path: &'a Path,
    haystack: R,
    count: bool,
    invert_match: bool,
    line_number: bool,
}

impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
    /// Create a new searcher.
    ///
    /// `inp` is a reusable input buffer that is used as scratch space by this
    /// searcher.
    ///
    /// `printer` is used to output all results of searching.
    ///
    /// `grep` is the actual matcher.
    ///
    /// `path` is the file path being searched.
    ///
    /// `haystack` is a reader of text to search.
    pub fn new(
        inp: &'a mut InputBuffer,
        printer: &'a mut Printer<W>,
        grep: &'a Grep,
        path: &'a Path,
        haystack: R,
    ) -> Searcher<'a, R, W> {
        Searcher {
            inp: inp,
            printer: printer,
            grep: grep,
            path: path,
            haystack: haystack,
            count: false,
            invert_match: false,
            line_number: false,
        }
    }

    /// If enabled, searching will print a count instead of each match.
    ///
    /// Disabled by default.
    pub fn count(mut self, yes: bool) -> Self {
        self.count = yes;
        self
    }

    /// If enabled, matching is inverted so that lines that *don't* match the
    /// given pattern are treated as matches.
    pub fn invert_match(mut self, yes: bool) -> Self {
        self.invert_match = yes;
        self
    }

    /// If enabled, compute line numbers and prefix each line of output with
    /// them.
    pub fn line_number(mut self, yes: bool) -> Self {
        self.line_number = yes;
        self
    }

    /// Execute the search. Results are written to the printer and the total
    /// number of matches is returned.
    #[inline(never)]
    pub fn run(mut self) -> Result<u64, Error> {
        self.inp.reset();
        let mut match_count = 0;
        let mut line_count = if self.line_number { Some(0) } else { None };
        let mut mat = Match::default();
        loop {
            let ok = try!(self.inp.fill(&mut self.haystack).map_err(|err| {
                Error::from_io(err, &self.path)
            }));
            if !ok {
                break;
            }
            while self.inp.pos < self.inp.lastnl {
                let ok = self.grep.read_match(
                    &mut mat,
                    &mut self.inp.buf[..self.inp.lastnl],
                    self.inp.pos);
                if !ok {
                    if self.invert_match {
                        while let Some(pos) = memchr(b'\n', &self.inp.buf[self.inp.pos..self.inp.lastnl]) {
                            if let Some(ref mut line_count) = line_count {
                                *line_count += 1;
                            }
                            self.printer.matched(
                                &self.path,
                                &self.inp.buf,
                                self.inp.pos,
                                self.inp.pos + pos,
                                line_count,
                            );
                            self.inp.pos += pos + 1;
                            match_count += 1;
                            if self.inp.pos >= self.inp.lastnl {
                                break;
                            }
                        }
                        self.inp.pos = self.inp.lastnl;
                    } else if let Some(ref mut line_count) = line_count {
                        *line_count += count_lines(
                            &self.inp.buf[self.inp.pos..self.inp.lastnl]);
                    }
                    break;
                }
                if self.invert_match {
                    while let Some(pos) = memchr(b'\n', &self.inp.buf[self.inp.pos..mat.start()]) {
                        if let Some(ref mut line_count) = line_count {
                            *line_count += 1;
                        }
                        self.printer.matched(
                            &self.path,
                            &self.inp.buf,
                            self.inp.pos,
                            self.inp.pos + pos,
                            line_count,
                        );
                        self.inp.pos += pos + 1;
                        match_count += 1;
                    }
                    if let Some(ref mut line_count) = line_count {
                        *line_count += 1;
                    }
                    self.inp.pos = mat.end() + 1;
                } else {
                    if let Some(ref mut line_count) = line_count {
                        // mat.end() always points immediately after the end
                        // of a match, which could be *at* a nl or past our
                        // current search buffer. Either way, count it as one
                        // more line.
                        *line_count += 1 + count_lines(
                            &self.inp.buf[self.inp.pos..mat.end()]);
                    }
                    match_count += 1;
                    if !self.count {
                        self.printer.matched(
                            self.path,
                            &self.inp.buf,
                            mat.start(),
                            mat.end(),
                            line_count,
                        );
                    }
                    // Move the position one past the end of the match so that
                    // the next search starts after the nl. If we're at EOF,
                    // then pos will be past EOF.
                    self.inp.pos = mat.end() + 1;
                }
            }
        }
        if self.count && match_count > 0 {
            self.printer.path_count(self.path, match_count);
        }
        Ok(match_count)
    }
}

pub struct InputBuffer {
    buf: Vec<u8>,
    tmp: Vec<u8>,
    pos: usize,
    lastnl: usize,
    end: usize,
    first: bool,
    is_binary: bool,
}

impl InputBuffer {
    /// Create a new buffer with a default capacity.
    pub fn new() -> InputBuffer {
        InputBuffer::with_capacity(READ_SIZE)
    }

    /// Create a new buffer with the capacity given.
    ///
    /// The capacity determines the size of each read from the underlying
    /// reader.
    pub fn with_capacity(cap: usize) -> InputBuffer {
        InputBuffer {
            buf: vec![0; cap],
            tmp: vec![],
            pos: 0,
            lastnl: 0,
            end: 0,
            first: true,
            is_binary: false,
        }
    }

    fn reset(&mut self) {
        self.pos = 0;
        self.lastnl = 0;
        self.end = 0;
        self.first = true;
        self.is_binary = false;
    }

    fn fill<R: io::Read>(&mut self, rdr: &mut R) -> Result<bool, io::Error> {
        if self.lastnl < self.end {
            self.tmp.clear();
            self.tmp.extend_from_slice(&self.buf[self.lastnl..self.end]);
            self.buf[0..self.tmp.len()].copy_from_slice(&self.tmp);
            self.end = self.tmp.len();
        } else {
            self.end = 0;
        }
        self.pos = 0;
        self.lastnl = 0;
        while self.lastnl == 0 {
            if self.buf.len() - self.end < READ_SIZE {
                let min_len = READ_SIZE + self.buf.len() - self.end;
                let new_len = cmp::max(min_len, self.buf.len() * 2);
                self.buf.resize(new_len, 0);
            }
            let n = try!(rdr.read(
                &mut self.buf[self.end..self.end + READ_SIZE]));
            if self.first {
                if is_binary(&self.buf[self.end..self.end + n]) {
                    return Ok(false);
                }
            }
            self.first = false;
            if n == 0 {
                if self.end == 0 {
                    return Ok(false);
                }
                self.lastnl = self.end;
                break;
            }
            // We know there is no nl between self.start..self.end since:
            //   1) If this is the first iteration, then any bytes preceding
            //      self.end do not contain nl by construction.
            //   2) Subsequent iterations only occur if no nl could be found.
            self.lastnl =
                memrchr(b'\n', &self.buf[self.end..self.end + n])
                .map(|i| self.end + i + 1)
                .unwrap_or(0);
            self.end += n;
        }
        Ok(true)
    }
}

fn is_binary(buf: &[u8]) -> bool {
    if buf.len() >= 4 && &buf[0..4] == b"%PDF" {
        return true;
    }
    memchr(b'\x00', &buf[0..cmp::min(1024, buf.len())]).is_some()
}

fn count_lines(mut buf: &[u8]) -> u64 {
    let mut count = 0;
    while let Some(pos) = memchr(b'\n', buf) {
        count += 1;
        buf = &buf[pos + 1..];
    }
    count
}
<commit_msg>tests and refactoring search<commit_after>/*!
The search module is responsible for searching a single file and printing
matches.
*/

use std::cmp;
use std::error::Error as StdError;
use std::fmt;
use std::io;
use std::path::{Path, PathBuf};

use grep::{Grep, Match};
use memchr::{memchr, memrchr};

use printer::Printer;

/// The default read size (capacity of input buffer).
const READ_SIZE: usize = 8 * (1<<10);

/// Error describes errors that can occur while searching.
#[derive(Debug)]
pub enum Error {
    /// Normal IO or Mmap errors suck. Include the path the originated them.
    Io {
        err: io::Error,
        path: PathBuf,
    }
}

impl Error {
    fn from_io<P: AsRef<Path>>(err: io::Error, path: P) -> Error {
        Error::Io { err: err, path: path.as_ref().to_path_buf() }
    }
}

impl StdError for Error {
    fn description(&self) -> &str {
        match *self {
            Error::Io { ref err, .. } => err.description(),
        }
    }

    fn cause(&self) -> Option<&StdError> {
        match *self {
            Error::Io { ref err, .. } => Some(err),
        }
    }
}

impl fmt::Display for Error {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        match *self {
            Error::Io { ref err, ref path } => {
                write!(f, "{}: {}", path.display(), err)
            }
        }
    }
}

pub struct Searcher<'a, R, W: 'a> {
    inp: &'a mut InputBuffer,
    printer: &'a mut Printer<W>,
    grep: &'a Grep,
    path: &'a Path,
    haystack: R,
    match_count: u64,
    line_count: Option<u64>,
    last_match: Match,
    count: bool,
    invert_match: bool,
    line_number: bool,
}

impl<'a, R: io::Read, W: io::Write> Searcher<'a, R, W> {
    /// Create a new searcher.
    ///
    /// `inp` is a reusable input buffer that is used as scratch space by this
    /// searcher.
    ///
    /// `printer` is used to output all results of searching.
    ///
    /// `grep` is the actual matcher.
    ///
    /// `path` is the file path being searched.
    ///
    /// `haystack` is a reader of text to search.
    pub fn new(
        inp: &'a mut InputBuffer,
        printer: &'a mut Printer<W>,
        grep: &'a Grep,
        path: &'a Path,
        haystack: R,
    ) -> Searcher<'a, R, W> {
        Searcher {
            inp: inp,
            printer: printer,
            grep: grep,
            path: path,
            haystack: haystack,
            match_count: 0,
            line_count: None,
            last_match: Match::default(),
            count: false,
            invert_match: false,
            line_number: false,
        }
    }

    /// If enabled, searching will print a count instead of each match.
    ///
    /// Disabled by default.
    pub fn count(mut self, yes: bool) -> Self {
        self.count = yes;
        self
    }

    /// If enabled, matching is inverted so that lines that *don't* match the
    /// given pattern are treated as matches.
    pub fn invert_match(mut self, yes: bool) -> Self {
        self.invert_match = yes;
        self
    }

    /// If enabled, compute line numbers and prefix each line of output with
    /// them.
    pub fn line_number(mut self, yes: bool) -> Self {
        self.line_number = yes;
        self
    }

    /// Execute the search. Results are written to the printer and the total
    /// number of matches is returned.
    #[inline(never)]
    pub fn run(mut self) -> Result<u64, Error> {
        self.inp.reset();
        self.match_count = 0;
        self.line_count = if self.line_number { Some(0) } else { None };
        self.last_match = Match::default();
        loop {
            let ok = try!(self.inp.fill(&mut self.haystack).map_err(|err| {
                Error::from_io(err, &self.path)
            }));
            if !ok {
                break;
            }
            while self.inp.pos < self.inp.lastnl {
                let ok = self.grep.read_match(
                    &mut self.last_match,
                    &mut self.inp.buf[..self.inp.lastnl],
                    self.inp.pos);
                if !ok {
                    let upto = self.inp.lastnl;
                    if self.invert_match {
                        self.find_inverted_matches(upto);
                    } else {
                        self.count_lines(upto);
                    }
                    self.inp.pos = upto;
                    break;
                }
                if self.invert_match {
                    let inverted_upto = self.last_match.start();
                    self.find_inverted_matches(inverted_upto);
                    // Add a line to account for the match...
                    if let Some(ref mut line_count) = self.line_count {
                        *line_count += 1;
                    }
                    // ... and skip over the match.
                    self.inp.pos = self.last_match.end() + 1;
                } else {
                    self.match_count += 1;
                    if !self.count {
                        let upto = cmp::min(
                            self.inp.lastnl, self.last_match.end() + 1);
                        self.count_lines(upto);
                        self.printer.matched(
                            self.path,
                            &self.inp.buf,
                            self.last_match.start(),
                            self.last_match.end(),
                            self.line_count,
                        );
                    }
                    // Move the position one past the end of the match so that
                    // the next search starts after the nl. If we're at EOF,
                    // then pos will be past EOF.
                    self.inp.pos = self.last_match.end() + 1;
                }
            }
        }
        if self.count && self.match_count > 0 {
            self.printer.path_count(self.path, self.match_count);
        }
        Ok(self.match_count)
    }

    #[inline(always)]
    fn find_inverted_matches(&mut self, upto: usize) {
        debug_assert!(self.invert_match);
        while self.inp.pos < upto {
            let pos = memchr(b'\n', &self.inp.buf[self.inp.pos..upto])
                      .unwrap_or(upto);
            if !self.count {
                if let Some(ref mut line_count) = self.line_count {
                    *line_count += 1;
                }
                self.printer.matched(
                    &self.path,
                    &self.inp.buf,
                    self.inp.pos,
                    self.inp.pos + pos,
                    self.line_count,
                );
            }
            self.inp.pos += pos + 1;
            self.match_count += 1;
        }
    }

    #[inline(always)]
    fn count_lines(&mut self, upto: usize) {
        if let Some(ref mut line_count) = self.line_count {
            *line_count += count_lines(&self.inp.buf[self.inp.pos..upto]);
        }
    }
}

pub struct InputBuffer {
    read_size: usize,
    buf: Vec<u8>,
    tmp: Vec<u8>,
    pos: usize,
    lastnl: usize,
    end: usize,
    first: bool,
    is_binary: bool,
}

impl InputBuffer {
    /// Create a new buffer with a default capacity.
    pub fn new() -> InputBuffer {
        InputBuffer::with_capacity(READ_SIZE)
    }

    /// Create a new buffer with the capacity given.
    ///
    /// The capacity determines the size of each read from the underlying
    /// reader.
    pub fn with_capacity(mut cap: usize) -> InputBuffer {
        if cap == 0 {
            cap = 1;
        }
        InputBuffer {
            read_size: cap,
            buf: vec![0; cap],
            tmp: vec![],
            pos: 0,
            lastnl: 0,
            end: 0,
            first: true,
            is_binary: false,
        }
    }

    fn reset(&mut self) {
        self.pos = 0;
        self.lastnl = 0;
        self.end = 0;
        self.first = true;
        self.is_binary = false;
    }

    fn fill<R: io::Read>(&mut self, rdr: &mut R) -> Result<bool, io::Error> {
        if self.lastnl < self.end {
            self.tmp.clear();
            self.tmp.extend_from_slice(&self.buf[self.lastnl..self.end]);
            self.buf[0..self.tmp.len()].copy_from_slice(&self.tmp);
            self.end = self.tmp.len();
        } else {
            self.end = 0;
        }
        self.pos = 0;
        self.lastnl = 0;
        while self.lastnl == 0 {
            if self.buf.len() - self.end < self.read_size {
                let min_len = self.read_size + self.buf.len() - self.end;
                let new_len = cmp::max(min_len, self.buf.len() * 2);
                self.buf.resize(new_len, 0);
            }
            let n = try!(rdr.read(
                &mut self.buf[self.end..self.end + self.read_size]));
            if self.first {
                if is_binary(&self.buf[self.end..self.end + n]) {
                    return Ok(false);
                }
            }
            self.first = false;
            if n == 0 {
                if self.end == 0 {
                    return Ok(false);
                }
                self.lastnl = self.end;
                break;
            }
            // We know there is no nl between self.start..self.end since:
            //   1) If this is the first iteration, then any bytes preceding
            //      self.end do not contain nl by construction.
            //   2) Subsequent iterations only occur if no nl could be found.
            self.lastnl =
                memrchr(b'\n', &self.buf[self.end..self.end + n])
                .map(|i| self.end + i + 1)
                .unwrap_or(0);
            self.end += n;
        }
        Ok(true)
    }
}

#[inline(always)]
fn is_binary(buf: &[u8]) -> bool {
    if buf.len() >= 4 && &buf[0..4] == b"%PDF" {
        return true;
    }
    memchr(b'\x00', &buf[0..cmp::min(1024, buf.len())]).is_some()
}

#[inline(always)]
fn count_lines(mut buf: &[u8]) -> u64 {
    let mut count = 0;
    while let Some(pos) = memchr(b'\n', buf) {
        count += 1;
        buf = &buf[pos + 1..];
    }
    count
}

#[cfg(test)]
mod tests {
    use std::io;
    use std::path::Path;

    use grep::{Grep, GrepBuilder};

    use printer::Printer;

    use super::{InputBuffer, Searcher};

    fn hay(s: &str) -> io::Cursor<Vec<u8>> {
        io::Cursor::new(s.to_string().into_bytes())
    }

    fn matcher(pat: &str) -> Grep {
        GrepBuilder::new(pat).build().unwrap()
    }

    fn test_path() -> &'static Path {
        &Path::new("/baz.rs")
    }

    #[test]
    fn basic_search() {
        let text = hay("\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
        let mut inp = InputBuffer::with_capacity(1);
        let mut pp = Printer::new(vec![]);
        let grep = matcher("Sherlock");
        let count = {
            let searcher = Searcher::new(
                &mut inp, &mut pp, &grep, test_path(), text);
            searcher.run().unwrap()
        };
        assert_eq!(2, count);
        let out = String::from_utf8(pp.into_inner()).unwrap();
        assert_eq!(out, "\
/baz.rs:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs:be, to a very large extent, the result of luck. Sherlock Holmes
");
    }

    #[test]
    fn line_numbers() {
        let text = hay("\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
        let mut inp = InputBuffer::with_capacity(1);
        let mut pp = Printer::new(vec![]);
        let grep = matcher("Sherlock");
        let count = {
            let searcher = Searcher::new(
                &mut inp, &mut pp, &grep, test_path(), text);
            searcher.line_number(true).run().unwrap()
        };
        assert_eq!(2, count);
        let out = String::from_utf8(pp.into_inner()).unwrap();
        assert_eq!(out, "\
/baz.rs:1:For the Doctor Watsons of this world, as opposed to the Sherlock
/baz.rs:3:be, to a very large extent, the result of luck. Sherlock Holmes
");
    }

    #[test]
    fn count() {
        let text = hay("\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
        let mut inp = InputBuffer::with_capacity(1);
        let mut pp = Printer::new(vec![]);
        let grep = matcher("Sherlock");
        let count = {
            let searcher = Searcher::new(
                &mut inp, &mut pp, &grep, test_path(), text);
            searcher.count(true).run().unwrap()
        };
        assert_eq!(2, count);
        let out = String::from_utf8(pp.into_inner()).unwrap();
        assert_eq!(out, "/baz.rs:2\n");
    }

    #[test]
    fn invert_match() {
        let text = hay("\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
        let mut inp = InputBuffer::with_capacity(1);
        let mut pp = Printer::new(vec![]);
        let grep = matcher("Sherlock");
        let count = {
            let searcher = Searcher::new(
                &mut inp, &mut pp, &grep, test_path(), text);
            searcher.invert_match(true).run().unwrap()
        };
        assert_eq!(4, count);
        let out = String::from_utf8(pp.into_inner()).unwrap();
        assert_eq!(out, "\
/baz.rs:Holmeses, success in the province of detective work must always
/baz.rs:can extract a clew from a wisp of straw or a flake of cigar ash;
/baz.rs:but Doctor Watson has to have it taken out for him and dusted,
/baz.rs:and exhibited clearly, with a label attached.
");
    }

    #[test]
    fn invert_match_line_numbers() {
        let text = hay("\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
        let mut inp = InputBuffer::with_capacity(1);
        let mut pp = Printer::new(vec![]);
        let grep = matcher("Sherlock");
        let count = {
            let searcher = Searcher::new(
                &mut inp, &mut pp, &grep, test_path(), text);
            searcher.invert_match(true).line_number(true).run().unwrap()
        };
        assert_eq!(4, count);
        let out = String::from_utf8(pp.into_inner()).unwrap();
        assert_eq!(out, "\
/baz.rs:2:Holmeses, success in the province of detective work must always
/baz.rs:4:can extract a clew from a wisp of straw or a flake of cigar ash;
/baz.rs:5:but Doctor Watson has to have it taken out for him and dusted,
/baz.rs:6:and exhibited clearly, with a label attached.
");
    }

    #[test]
    fn invert_match_count() {
        let text = hay("\
For the Doctor Watsons of this world, as opposed to the Sherlock
Holmeses, success in the province of detective work must always
be, to a very large extent, the result of luck. Sherlock Holmes
can extract a clew from a wisp of straw or a flake of cigar ash;
but Doctor Watson has to have it taken out for him and dusted,
and exhibited clearly, with a label attached.\
");
        let mut inp = InputBuffer::with_capacity(1);
        let mut pp = Printer::new(vec![]);
        let grep = matcher("Sherlock");
        let count = {
            let searcher = Searcher::new(
                &mut inp, &mut pp, &grep, test_path(), text);
            searcher.invert_match(true).count(true).run().unwrap()
        };
        assert_eq!(4, count);
        let out = String::from_utf8(pp.into_inner()).unwrap();
        assert_eq!(out, "/baz.rs:4\n");
    }
}
<|endoftext|>