<commit_before>use ansi_term::{ANSIString, Colour, Style};
use config::Config;

/// Merges in the default values for the program
pub fn merge_defaults(c: &mut Config) {
    c.set_default("global.modules", vec!["directory", "git", "prompt"]).unwrap();
    c.set_default("global.foreground", "bright_white").unwrap();
    c.set_default("global.background", "blue").unwrap();
    c.set_default("global.style", "default").unwrap();
    c.set_default("global.separator", "").unwrap();
    c.set_default("global.padding_left", " ").unwrap();
    c.set_default("global.padding_right", " ").unwrap();

    c.set_default("modules.directory.max_depth", 4).unwrap();

    c.set_default("modules.exit_code.bg_success", "green").unwrap();
    c.set_default("modules.exit_code.bg_error", "red").unwrap();

    c.set_default("modules.git.symbol_insertion", "+").unwrap();
    c.set_default("modules.git.symbol_deletion", "-").unwrap();
    c.set_default("modules.git.symbol_push", "⇡").unwrap();
    c.set_default("modules.git.symbol_pull", "⇣").unwrap();
    c.set_default("modules.git.show_changed", true).unwrap();
    c.set_default("modules.git.show_diff_stats", false).unwrap();
    c.set_default("modules.git.show_unpushed", true).unwrap();

    c.set_default("modules.prompt.output", "$").unwrap();
    c.set_default("modules.prompt.bg_success", "green").unwrap();
    c.set_default("modules.prompt.bg_error", "red").unwrap();
}

/// Generic formatting method
pub fn format_module<'a>(c: &mut Config,
                         name: &'a str,
                         output: Option<String>,
                         last_successful: Option<&str>)
                         -> (Option<&'a str>, Option<ANSIString<'static>>) {
    // Formatting was not successful if there was nothing to format
    if c.get_str(&format!("modules.{}.output", name)).is_none() && output.is_none() {
        return (None, None);
    }

    // Get config options
    let fg = c.get_str(&format!("modules.{}.foreground", name))
        .unwrap_or_else(|| c.get_str("global.foreground").unwrap_or_default());
    let fg = string_to_colour(fg);

    let bg = c.get_str(&format!("modules.{}.background", name))
        .unwrap_or_else(|| c.get_str("global.background").unwrap_or_default());
    let bg = string_to_colour(bg);

    let padding_left = c.get_str(&format!("modules.{}.padding_left", name))
        .unwrap_or_else(|| c.get_str("global.padding_left").unwrap_or_default());
    let padding_right = c.get_str(&format!("modules.{}.padding_right", name))
        .unwrap_or_else(|| c.get_str("global.padding_right").unwrap_or_default());
    let separator = c.get_str(&format!("modules.{}.separator", name))
        .unwrap_or_else(|| c.get_str("global.separator").unwrap_or_default());

    let main_style = c.get_str(&format!("modules.{}.style", name))
        .unwrap_or_else(|| c.get_str("global.style").unwrap_or_default());
    let main_style = string_to_style(main_style).on(bg).fg(fg);

    // Override the output if there is "output" in this module's config
    let output = if let Some(s) = c.get_str(&format!("modules.{}.output", name)) {
        s
    } else {
        output.unwrap()
    };

    // Format the main content
    let mut content = format!("\\[{}\\]{}{}{}\\[{}\\]",
                              main_style.prefix(),
                              padding_left,
                              output,
                              padding_right,
                              main_style.suffix());

    // Format the separator
    let main_style = main_style.on(fg).fg(bg);
    if let Some(name) = last_successful {
        // There is a visible module that comes after this one
        let next_bg = c.get_str(&format!("modules.{}.background", name))
            .unwrap_or_else(|| c.get_str("global.background").unwrap_or_default());
        let next_bg = string_to_colour(next_bg);

        content = format!("{}\\[{}\\]{}\\[{}\\]",
                          content,
                          main_style.on(next_bg).prefix(),
                          separator,
                          main_style.on(next_bg).suffix());
    } else {
        // This is the final module
        content = format!("{}\\[{}\\]{}\\[{}\\]",
                          content,
                          bg.prefix(),
                          separator,
                          bg.suffix());
    }

    (Some(name), Some(ANSIString::from(content)))
}

// Converts a string (from the config file) to a Colour
// See: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
fn string_to_colour(s: String) -> Colour {
    if let Ok(i) = s.parse::<u8>() {
        Colour::Fixed(i)
    } else {
        let s = s.to_lowercase();
        match s.as_ref() {
            "black" => Colour::Fixed(0),
            "bright_black" => Colour::Fixed(8),
            "red" => Colour::Fixed(1),
            "bright_red" => Colour::Fixed(9),
            "green" => Colour::Fixed(2),
            "bright_green" => Colour::Fixed(10),
            "yellow" => Colour::Fixed(3),
            "bright_yellow" => Colour::Fixed(11),
            "blue" => Colour::Fixed(4),
            "bright_blue" => Colour::Fixed(12),
            "purple" => Colour::Fixed(5),
            "bright_purple" => Colour::Fixed(13),
            "cyan" => Colour::Fixed(6),
            "bright_cyan" => Colour::Fixed(14),
            "white" => Colour::Fixed(7),
            "bright_white" => Colour::Fixed(15),
            _ => panic!("Invalid color option: {} in config file!", s),
        }
    }
}

fn string_to_style(s: String) -> Style {
    let s = s.to_lowercase();
    match s.as_ref() {
        "default" | "" | "normal" => Style::new(),
        "bold" => Style::new().bold(),
        "dimmed" => Style::new().dimmed(),
        "italic" => Style::new().italic(),
        "underline" => Style::new().underline(),
        "blink" => Style::new().blink(),
        "reverse" => Style::new().reverse(),
        "hidden" => Style::new().hidden(),
        "strikethrough" => Style::new().strikethrough(),
        _ => panic!("Unknown style property: {} in config file!", s),
    }
}

pub fn format_module_prompt<'a>(c: &mut Config,
                                last_successful: Option<&'a str>,
                                exit_code: &str)
                                -> (Option<&'a str>, Option<ANSIString<'static>>) {
    let bg = match exit_code.as_ref() {
        "0" => c.get_str("modules.prompt.bg_success").unwrap_or_default(),
        _ => c.get_str("modules.prompt.bg_error").unwrap_or_default(),
    };
    c.set("modules.prompt.background", bg).unwrap();

    let output = c.get_str("modules.prompt.output").unwrap_or_default();

    format_module(c, "prompt", Some(output), last_successful)
}

pub fn format_module_exit_code<'a>(c: &mut Config,
                                   last_successful: Option<&'a str>,
                                   exit_code: &str)
                                   -> (Option<&'a str>, Option<ANSIString<'static>>) {
    let bg = match exit_code.as_ref() {
        "0" => c.get_str("modules.exit_code.bg_success").unwrap_or_default(),
        _ => c.get_str("modules.exit_code.bg_error").unwrap_or_default(),
    };
    c.set("modules.exit_code.background", bg).unwrap();

    format_module(c, "exit_code", Some(exit_code.to_string()), last_successful)
}

pub fn format_module_directory<'a>(c: &mut Config,
                                   last_successful: Option<&'a str>)
                                   -> (Option<&'a str>, Option<ANSIString<'static>>) {
    use std::env;
    use std::path::PathBuf;

    let home = env::var("HOME").unwrap();
    let cwd = env::current_dir().unwrap();

    // Convert "/home/user/directory" to "~/directory"
    let mut shortened_cwd: PathBuf;
    if let Ok(stripped_cwd) = cwd.strip_prefix(&home) {
        shortened_cwd = PathBuf::from("~").join(stripped_cwd);
    } else {
        shortened_cwd = env::current_dir().unwrap();
    }

    let depth = shortened_cwd.components().count();

    // Max number of directories we want to see
    let max_depth = c.get_int("modules.directory.max_depth").unwrap_or_default() as usize;

    if depth > max_depth {
        let comp_iter = shortened_cwd.clone();
        let comp_iter = comp_iter.components();

        shortened_cwd = PathBuf::new();
        for (i, component) in comp_iter.enumerate() {
            if i < (max_depth / 2) || i >= (depth - (max_depth / 2)) {
                shortened_cwd.push(component.as_os_str());
            } else if i == (max_depth / 2) {
                shortened_cwd.push("...");
            }
        }
    }

    format_module(c,
                  "directory",
                  Some(format!("{}", shortened_cwd.display())),
                  last_successful)
}

pub fn format_module_git<'a>(c: &mut Config,
                             last_successful: Option<&'a str>)
                             -> (Option<&'a str>, Option<ANSIString<'static>>) {
    use git2::{Branch, Repository};
    use std::env;

    let mut output = String::new();

    if let Ok(repo) = Repository::discover(env::current_dir().unwrap_or_default()) {
        let local = repo.head().unwrap();

        // Output current branch name
        output.push_str(local.shorthand().unwrap());

        // Show local changes
        let show_diffs = c.get_bool("modules.git.show_diff_stats").unwrap_or_default();
        let show_changed = c.get_bool("modules.git.show_changed").unwrap_or_default();
        if show_changed {
            let diff_stats = repo.diff_index_to_workdir(None, None).unwrap();
            let diff_stats = diff_stats.stats().unwrap();

            if diff_stats.files_changed() > 0 {
                let symbol_insertion = c.get_str("modules.git.symbol_insertion")
                    .unwrap_or_default();

                if show_diffs {
                    // Show actual number of insertions or deletions
                    let symbol_deletion = c.get_str("modules.git.symbol_deletion")
                        .unwrap_or_default();
                    output.push_str(&format!(" ({}{}, {}{})",
                                             symbol_deletion,
                                             diff_stats.deletions(),
                                             symbol_insertion,
                                             diff_stats.insertions()));
                } else {
                    // Do not show insertions nor deletions, just say
                    // something was changed
                    output.push_str(&format!(" {}", symbol_insertion));
                }
            }
        }

        let local = Branch::wrap(local);

        // Show unpushed/unpulled commits
        let show_unpushed = c.get_bool("modules.git.show_unpushed").unwrap_or_default();
        if show_unpushed {
            if let Ok(upstream) = local.upstream() {
                let local_ref = local.get();
                let upstream_ref = upstream.get();

                if let Ok((ahead, behind)) =
                    repo.graph_ahead_behind(local_ref.target().unwrap(),
                                            upstream_ref.target().unwrap()) {

                    if ahead != 0 {
                        let symbol_push = c.get_str("modules.git.symbol_push").unwrap_or_default();
                        output.push_str(&format!(" {}{}", symbol_push, ahead));
                    }

                    if behind != 0 {
                        let symbol_pull = c.get_str("modules.git.symbol_pull").unwrap_or_default();
                        output.push_str(&format!(" {}{}", symbol_pull, behind));
                    }
                }
            }
        }
    }

    if output.is_empty() {
        (None, None)
    } else {
        format_module(c, "git", Some(output), last_successful)
    }
}

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

    // Note: ansi_term has already tested this kind of thing, this is
    // just to ensure that passing a string and matching on it works
    // like I expect (sanity-check)

    #[test]
    fn test_colour_and_style() {
        const CONTENT: &'static str = "hi";

        struct Test<'a> {
            color: &'static str,
            style: &'static str,
            expected: &'a str,
        }

        let tests = [Test {
                         color: "blue",
                         style: "bold",
                         expected: &format!("\x1B[1;38;5;4m{}\x1B[0m", CONTENT),
                     },
                     Test {
                         color: "bright_white",
                         style: "",
                         expected: &format!("\x1B[38;5;15m{}\x1B[0m", CONTENT),
                     }];

        for test in &tests {
            let style = string_to_style(test.style.to_string());
            let color = string_to_colour(test.color.to_string());
            let result = format!("{}", style.fg(color).paint(CONTENT));

            assert_eq!(test.expected, result);
        }
    }

    #[test]
    fn test_string_to_style() {
        const CONTENT: &'static str = "hi";

        struct Test<'a> {
            input: &'static str,
            expected: &'a str,
        }

        let tests = [Test {
                         input: "bold",
                         expected: &format!("\x1B[1m{}\x1B[0m", CONTENT),
                     },
                     Test {
                         input: "italic",
                         expected: &format!("\x1B[3m{}\x1B[0m", CONTENT),
                     }];

        for test in &tests {
            let result = string_to_style(test.input.to_string());
            let result = format!("{}", result.paint(CONTENT));

            assert_eq!(test.expected, result);
        }
    }

    #[test]
    #[should_panic]
    fn test_string_to_style_invalid_input() {
        struct Test {
            input: &'static str,
        }

        let tests = [Test { input: "bold" }, Test { input: "invalid" }];

        for test in &tests {
            string_to_style(test.input.to_string());
        }
    }

    #[test]
    fn test_string_to_colour() {
        const CONTENT: &'static str = "hi";

        struct Test<'a> {
            input: &'static str,
            expected: &'a str,
        }

        let tests = [Test {
                         input: "green",
                         expected: &format!("\x1B[38;5;2m{}\x1B[0m", CONTENT),
                     },
                     Test {
                         input: "bright_green",
                         expected: &format!("\x1B[38;5;10m{}\x1B[0m", CONTENT),
                     }];

        for test in &tests {
            let result = string_to_colour(test.input.to_string());
            let result = format!("{}", result.paint(CONTENT));

            assert_eq!(test.expected, result);
        }
    }

    #[test]
    #[should_panic]
    fn test_string_to_colour_invalid_input() {
        struct Test {
            input: &'static str,
        }

        let tests = [Test { input: "green" }, Test { input: "invalid" }];

        for test in &tests {
            string_to_colour(test.input.to_string());
        }
    }
}
<commit_msg>Fix compilation error with env::current_dir()<commit_after>use ansi_term::{ANSIString, Colour, Style};
use config::Config;

/// Merges in the default values for the program
pub fn merge_defaults(c: &mut Config) {
    c.set_default("global.modules", vec!["directory", "git", "prompt"]).unwrap();
    c.set_default("global.foreground", "bright_white").unwrap();
    c.set_default("global.background", "blue").unwrap();
    c.set_default("global.style", "default").unwrap();
    c.set_default("global.separator", "").unwrap();
    c.set_default("global.padding_left", " ").unwrap();
    c.set_default("global.padding_right", " ").unwrap();

    c.set_default("modules.directory.max_depth", 4).unwrap();

    c.set_default("modules.exit_code.bg_success", "green").unwrap();
    c.set_default("modules.exit_code.bg_error", "red").unwrap();

    c.set_default("modules.git.symbol_insertion", "+").unwrap();
    c.set_default("modules.git.symbol_deletion", "-").unwrap();
    c.set_default("modules.git.symbol_push", "⇡").unwrap();
    c.set_default("modules.git.symbol_pull", "⇣").unwrap();
    c.set_default("modules.git.show_changed", true).unwrap();
    c.set_default("modules.git.show_diff_stats", false).unwrap();
    c.set_default("modules.git.show_unpushed", true).unwrap();

    c.set_default("modules.prompt.output", "$").unwrap();
    c.set_default("modules.prompt.bg_success", "green").unwrap();
    c.set_default("modules.prompt.bg_error", "red").unwrap();
}

/// Generic formatting method
pub fn format_module<'a>(c: &mut Config,
                         name: &'a str,
                         output: Option<String>,
                         last_successful: Option<&str>)
                         -> (Option<&'a str>, Option<ANSIString<'static>>) {
    // Formatting was not successful if there was nothing to format
    if c.get_str(&format!("modules.{}.output", name)).is_none() && output.is_none() {
        return (None, None);
    }

    // Get config options
    let fg = c.get_str(&format!("modules.{}.foreground", name))
        .unwrap_or_else(|| c.get_str("global.foreground").unwrap_or_default());
    let fg = string_to_colour(fg);

    let bg = c.get_str(&format!("modules.{}.background", name))
        .unwrap_or_else(|| c.get_str("global.background").unwrap_or_default());
    let bg = string_to_colour(bg);

    let padding_left = c.get_str(&format!("modules.{}.padding_left", name))
        .unwrap_or_else(|| c.get_str("global.padding_left").unwrap_or_default());
    let padding_right = c.get_str(&format!("modules.{}.padding_right", name))
        .unwrap_or_else(|| c.get_str("global.padding_right").unwrap_or_default());
    let separator = c.get_str(&format!("modules.{}.separator", name))
        .unwrap_or_else(|| c.get_str("global.separator").unwrap_or_default());

    let main_style = c.get_str(&format!("modules.{}.style", name))
        .unwrap_or_else(|| c.get_str("global.style").unwrap_or_default());
    let main_style = string_to_style(main_style).on(bg).fg(fg);

    // Override the output if there is "output" in this module's config
    let output = if let Some(s) = c.get_str(&format!("modules.{}.output", name)) {
        s
    } else {
        output.unwrap()
    };

    // Format the main content
    let mut content = format!("\\[{}\\]{}{}{}\\[{}\\]",
                              main_style.prefix(),
                              padding_left,
                              output,
                              padding_right,
                              main_style.suffix());

    // Format the separator
    let main_style = main_style.on(fg).fg(bg);
    if let Some(name) = last_successful {
        // There is a visible module that comes after this one
        let next_bg = c.get_str(&format!("modules.{}.background", name))
            .unwrap_or_else(|| c.get_str("global.background").unwrap_or_default());
        let next_bg = string_to_colour(next_bg);

        content = format!("{}\\[{}\\]{}\\[{}\\]",
                          content,
                          main_style.on(next_bg).prefix(),
                          separator,
                          main_style.on(next_bg).suffix());
    } else {
        // This is the final module
        content = format!("{}\\[{}\\]{}\\[{}\\]",
                          content,
                          bg.prefix(),
                          separator,
                          bg.suffix());
    }

    (Some(name), Some(ANSIString::from(content)))
}

// Converts a string (from the config file) to a Colour
// See: https://upload.wikimedia.org/wikipedia/commons/1/15/Xterm_256color_chart.svg
fn string_to_colour(s: String) -> Colour {
    if let Ok(i) = s.parse::<u8>() {
        Colour::Fixed(i)
    } else {
        let s = s.to_lowercase();
        match s.as_ref() {
            "black" => Colour::Fixed(0),
            "bright_black" => Colour::Fixed(8),
            "red" => Colour::Fixed(1),
            "bright_red" => Colour::Fixed(9),
            "green" => Colour::Fixed(2),
            "bright_green" => Colour::Fixed(10),
            "yellow" => Colour::Fixed(3),
            "bright_yellow" => Colour::Fixed(11),
            "blue" => Colour::Fixed(4),
            "bright_blue" => Colour::Fixed(12),
            "purple" => Colour::Fixed(5),
            "bright_purple" => Colour::Fixed(13),
            "cyan" => Colour::Fixed(6),
            "bright_cyan" => Colour::Fixed(14),
            "white" => Colour::Fixed(7),
            "bright_white" => Colour::Fixed(15),
            _ => panic!("Invalid color option: {} in config file!", s),
        }
    }
}

fn string_to_style(s: String) -> Style {
    let s = s.to_lowercase();
    match s.as_ref() {
        "default" | "" | "normal" => Style::new(),
        "bold" => Style::new().bold(),
        "dimmed" => Style::new().dimmed(),
        "italic" => Style::new().italic(),
        "underline" => Style::new().underline(),
        "blink" => Style::new().blink(),
        "reverse" => Style::new().reverse(),
        "hidden" => Style::new().hidden(),
        "strikethrough" => Style::new().strikethrough(),
        _ => panic!("Unknown style property: {} in config file!", s),
    }
}

pub fn format_module_prompt<'a>(c: &mut Config,
                                last_successful: Option<&'a str>,
                                exit_code: &str)
                                -> (Option<&'a str>, Option<ANSIString<'static>>) {
    let bg = match exit_code.as_ref() {
        "0" => c.get_str("modules.prompt.bg_success").unwrap_or_default(),
        _ => c.get_str("modules.prompt.bg_error").unwrap_or_default(),
    };
    c.set("modules.prompt.background", bg).unwrap();

    let output = c.get_str("modules.prompt.output").unwrap_or_default();

    format_module(c, "prompt", Some(output), last_successful)
}

pub fn format_module_exit_code<'a>(c: &mut Config,
                                   last_successful: Option<&'a str>,
                                   exit_code: &str)
                                   -> (Option<&'a str>, Option<ANSIString<'static>>) {
    let bg = match exit_code.as_ref() {
        "0" => c.get_str("modules.exit_code.bg_success").unwrap_or_default(),
        _ => c.get_str("modules.exit_code.bg_error").unwrap_or_default(),
    };
    c.set("modules.exit_code.background", bg).unwrap();

    format_module(c, "exit_code", Some(exit_code.to_string()), last_successful)
}

pub fn format_module_directory<'a>(c: &mut Config,
                                   last_successful: Option<&'a str>)
                                   -> (Option<&'a str>, Option<ANSIString<'static>>) {
    use std::env;
    use std::path::PathBuf;

    let home = env::var("HOME").unwrap();
    let cwd = env::current_dir().unwrap();

    // Convert "/home/user/directory" to "~/directory"
    let mut shortened_cwd: PathBuf;
    if let Ok(stripped_cwd) = cwd.strip_prefix(&home) {
        shortened_cwd = PathBuf::from("~").join(stripped_cwd);
    } else {
        shortened_cwd = env::current_dir().unwrap();
    }

    let depth = shortened_cwd.components().count();

    // Max number of directories we want to see
    let max_depth = c.get_int("modules.directory.max_depth").unwrap_or_default() as usize;

    if depth > max_depth {
        let comp_iter = shortened_cwd.clone();
        let comp_iter = comp_iter.components();

        shortened_cwd = PathBuf::new();
        for (i, component) in comp_iter.enumerate() {
            if i < (max_depth / 2) || i >= (depth - (max_depth / 2)) {
                shortened_cwd.push(component.as_os_str());
            } else if i == (max_depth / 2) {
                shortened_cwd.push("...");
            }
        }
    }

    format_module(c,
                  "directory",
                  Some(format!("{}", shortened_cwd.display())),
                  last_successful)
}

pub fn format_module_git<'a>(c: &mut Config,
                             last_successful: Option<&'a str>)
                             -> (Option<&'a str>, Option<ANSIString<'static>>) {
    use git2::{Branch, Repository};
    use std::env;

    let mut output = String::new();

    if let Ok(repo) = Repository::discover(env::current_dir().unwrap()) {
        let local = repo.head().unwrap();

        // Output current branch name
        output.push_str(local.shorthand().unwrap());

        // Show local changes
        let show_diffs = c.get_bool("modules.git.show_diff_stats").unwrap_or_default();
        let show_changed = c.get_bool("modules.git.show_changed").unwrap_or_default();
        if show_changed {
            let diff_stats = repo.diff_index_to_workdir(None, None).unwrap();
            let diff_stats = diff_stats.stats().unwrap();

            if diff_stats.files_changed() > 0 {
                let symbol_insertion = c.get_str("modules.git.symbol_insertion")
                    .unwrap_or_default();

                if show_diffs {
                    // Show actual number of insertions or deletions
                    let symbol_deletion = c.get_str("modules.git.symbol_deletion")
                        .unwrap_or_default();
                    output.push_str(&format!(" ({}{}, {}{})",
                                             symbol_deletion,
                                             diff_stats.deletions(),
                                             symbol_insertion,
                                             diff_stats.insertions()));
                } else {
                    // Do not show insertions nor deletions, just say
                    // something was changed
                    output.push_str(&format!(" {}", symbol_insertion));
                }
            }
        }

        let local = Branch::wrap(local);

        // Show unpushed/unpulled commits
        let show_unpushed = c.get_bool("modules.git.show_unpushed").unwrap_or_default();
        if show_unpushed {
            if let Ok(upstream) = local.upstream() {
                let local_ref = local.get();
                let upstream_ref = upstream.get();

                if let Ok((ahead, behind)) =
                    repo.graph_ahead_behind(local_ref.target().unwrap(),
                                            upstream_ref.target().unwrap()) {

                    if ahead != 0 {
                        let symbol_push = c.get_str("modules.git.symbol_push").unwrap_or_default();
                        output.push_str(&format!(" {}{}", symbol_push, ahead));
                    }

                    if behind != 0 {
                        let symbol_pull = c.get_str("modules.git.symbol_pull").unwrap_or_default();
                        output.push_str(&format!(" {}{}", symbol_pull, behind));
                    }
                }
            }
        }
    }

    if output.is_empty() {
        (None, None)
    } else {
        format_module(c, "git", Some(output), last_successful)
    }
}

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

    // Note: ansi_term has already tested this kind of thing, this is
    // just to ensure that passing a string and matching on it works
    // like I expect (sanity-check)

    #[test]
    fn test_colour_and_style() {
        const CONTENT: &'static str = "hi";

        struct Test<'a> {
            color: &'static str,
            style: &'static str,
            expected: &'a str,
        }

        let tests = [Test {
                         color: "blue",
                         style: "bold",
                         expected: &format!("\x1B[1;38;5;4m{}\x1B[0m", CONTENT),
                     },
                     Test {
                         color: "bright_white",
                         style: "",
                         expected: &format!("\x1B[38;5;15m{}\x1B[0m", CONTENT),
                     }];

        for test in &tests {
            let style = string_to_style(test.style.to_string());
            let color = string_to_colour(test.color.to_string());
            let result = format!("{}", style.fg(color).paint(CONTENT));

            assert_eq!(test.expected, result);
        }
    }

    #[test]
    fn test_string_to_style() {
        const CONTENT: &'static str = "hi";

        struct Test<'a> {
            input: &'static str,
            expected: &'a str,
        }

        let tests = [Test {
                         input: "bold",
                         expected: &format!("\x1B[1m{}\x1B[0m", CONTENT),
                     },
                     Test {
                         input: "italic",
                         expected: &format!("\x1B[3m{}\x1B[0m", CONTENT),
                     }];

        for test in &tests {
            let result = string_to_style(test.input.to_string());
            let result = format!("{}", result.paint(CONTENT));

            assert_eq!(test.expected, result);
        }
    }

    #[test]
    #[should_panic]
    fn test_string_to_style_invalid_input() {
        struct Test {
            input: &'static str,
        }

        let tests = [Test { input: "bold" }, Test { input: "invalid" }];

        for test in &tests {
            string_to_style(test.input.to_string());
        }
    }

    #[test]
    fn test_string_to_colour() {
        const CONTENT: &'static str = "hi";

        struct Test<'a> {
            input: &'static str,
            expected: &'a str,
        }

        let tests = [Test {
                         input: "green",
                         expected: &format!("\x1B[38;5;2m{}\x1B[0m", CONTENT),
                     },
                     Test {
                         input: "bright_green",
                         expected: &format!("\x1B[38;5;10m{}\x1B[0m", CONTENT),
                     }];

        for test in &tests {
            let result = string_to_colour(test.input.to_string());
            let result = format!("{}", result.paint(CONTENT));

            assert_eq!(test.expected, result);
        }
    }

    #[test]
    #[should_panic]
    fn test_string_to_colour_invalid_input() {
        struct Test {
            input: &'static str,
        }

        let tests = [Test { input: "green" }, Test { input: "invalid" }];

        for test in &tests {
            string_to_colour(test.input.to_string());
        }
    }
}
<|endoftext|>


<commit_before>extern crate byteorder;

use commons::HASH_SIZE;
use pool::{ACTION_REQUEST_DATA, ACTION_REQUEST_DESCRIPTION, ACTION_SEND_DATA, ACTION_SEND_DESCRIPTION, MAGIC_NUMBER,
           PROTOCOL_VERSION};
use pool::Pool;
use self::byteorder::{BigEndian, ByteOrder};
use std::cmp::max;
use std::net::SocketAddr;
use std::net::UdpSocket;
use std::path::PathBuf;
use std::thread::sleep;
use std::time::{Duration, Instant};

pub struct Network
{
    socket: UdpSocket,
    client: bool,
    send_address: Vec<SocketAddr>,
    pool: Pool,
    megabit_per_second: f64,
}

impl Network
{
    fn sanity_check(&self, header: &[u8; 6]) -> bool
    {
        let mut buffer: [u8; 6] = [0; 6];
        BigEndian::write_u32(&mut buffer[0..4], MAGIC_NUMBER);
        BigEndian::write_u16(&mut buffer[4..6], PROTOCOL_VERSION);
        return header.clone() == buffer;
    }

    pub fn run(&mut self)
    {
        println!("Program started!");
        if self.client
        {
            let mut finished = false;
            'outer: while !finished
            {
                let mut started = Instant::now();
                while started.elapsed() < Duration::from_millis(2500)
                {
                    let mut buffer: [u8; 1 << 16] = [0; 1 << 16];
                    let buffer_size = match self.socket.recv_from(&mut buffer)
                    {
                        Err(_) => 0,
                        Ok(n) => n.0,
                    };
                    if buffer_size < 7
                    {
                        continue;
                    }
                    let mut head: [u8; 6] = [0; 6];
                    head.clone_from_slice(&buffer[0..6]);
                    if !self.sanity_check(&head)
                    {
                        continue;
                    }
                    if buffer[6] != ACTION_SEND_DESCRIPTION
                    {
                        continue;
                    }
                    let mut v = Vec::new();
                    v.extend_from_slice(&buffer[7..buffer_size]);
                    finished = self.pool.process_binary_description(&v);
                    if finished
                    {
                        break 'outer;
                    }
                    started = Instant::now();
                    println!("Asking for files description: {} left", self.pool.desc_left());
                }
                let mut send_request: Vec<u8> = Vec::new();
                let mut buffer: [u8; 128] = [0; 128];
                // Magic number
                BigEndian::write_u32(&mut buffer[0..4], MAGIC_NUMBER);
                send_request.extend_from_slice(&buffer[0..4]);
                // Protocol version
                BigEndian::write_u16(&mut buffer[0..2], PROTOCOL_VERSION);
                send_request.extend_from_slice(&buffer[0..2]);
                // Action
                send_request.push(ACTION_REQUEST_DESCRIPTION);
                let _ = match self.socket.send_to(send_request.as_slice(), &self.send_address[0])
                {
                    Err(why) => panic!("Cannot send request for description: {}", why),
                    Ok(n) => n,
                };
            }
        }
        let mut last_print = Instant::now();
        while self.client
        {
            if self.pool.is_complete()
            {
                return;
            }
            let mut buffer: [u8; 1 << 16] = [0; 1 << 16];
            let mut buffer_size;
            let mut count = 0;
            loop
            {
                if last_print.elapsed().as_secs() > 1
                {
                    last_print = Instant::now();
                    println!("~{} kibibytes left", self.pool.chunks_left() * 60000 / 1024);
                }
                if count > 40000 / HASH_SIZE
                {
                    break;
                }
                // TODO: if someone flood us with garbage this cycle never ends
                buffer_size = match self.socket.recv_from(&mut buffer)
                {
                    Err(_) => 0,
                    Ok(n) => n.0,
                };
                if buffer_size == 0
                {
                    break;
                }
                if buffer_size < 7
                {
                    continue;
                }
                let mut head: [u8; 6] = [0; 6];
                head.clone_from_slice(&buffer[0..6]);
                if !self.sanity_check(&head)
                {
                    continue;
                }
                if buffer[6] == ACTION_SEND_DATA
                {
                    count += 1;
                    let mut v: Vec<u8> = Vec::new();
                    v.extend_from_slice(&buffer[7..buffer_size]);
                    self.pool.process_binary_data(&v);
                    if self.pool.is_complete()
                    {
                        return;
                    }
                }
            }
            let send_req = self.pool.get_binary_chunk_request();
            let _ = self.socket.send_to(send_req.as_slice(), &self.send_address[0]);
        }
        let mut send_started = Instant::now();
        while !self.client
        {
            if last_print.elapsed().as_secs() > 1
            {
                last_print = Instant::now();
                println!("Queue length: {}", self.pool.get_queue_size());
            }
            let packet = self.pool.get_binary_send_packet();
            if packet.is_none()
            {
                let _ = self.socket.set_nonblocking(false);
            }
            else
            {
                let unwrapped_packet = packet.unwrap();
                for addr in &self.send_address
                {
                    let _ = self.socket.send_to(unwrapped_packet.as_slice(), addr);
                }
                let converted_size: f64 = (unwrapped_packet.len() as f64) * 8.0 / 1000000.0;
                let milli_stop: u64 = (converted_size * 1000.0 / self.megabit_per_second) as u64;
                let _ = self.socket.set_nonblocking(true);
                sleep(Duration::from_millis(milli_stop));
            }
            let mut buffer: [u8; 1 << 16] = [0; 1 << 16];
            let buffer_size = match self.socket.recv_from(&mut buffer)
            {
                Err(_) => 0,
                Ok(n) => n.0,
            };
            if buffer_size < 7
            {
                continue;
            }
            let mut head: [u8; 6] = [0; 6];
            head.clone_from_slice(&buffer[0..6]);
            if !self.sanity_check(&head)
            {
                continue;
            }
            if buffer[6] == ACTION_REQUEST_DATA
            {
                let mut v: Vec<u8> = Vec::new();
                v.extend_from_slice(&buffer[7..buffer_size]);
                self.pool.process_binary_chunk_request(&v);
            }
            if buffer[6] == ACTION_REQUEST_DESCRIPTION || send_started.elapsed().as_secs() >= 10
            {
                send_started = Instant::now();
                let b = self.pool.generate_binary_description();
                for bv in &b
                {
                    for addr in &self.send_address
                    {
                        let _ = self.socket.send_to(bv.as_slice(), addr);
                    }
                    sleep(max(Duration::from_millis((480.0 / self.megabit_per_second) as u64), Duration::from_millis(1)));
                }
            }
        }
    }

    pub fn new(files: &Vec<PathBuf>, _client: bool, _send_address: Vec<SocketAddr>, _megabit_per_second: f64) -> Network
    {
        println!("Welcome to Stardust");
        if _send_address.len() != 1
        {
            panic!("Cannot have multiple server addresses");
        }
        let sock = match if _client
        {
            UdpSocket::bind("0.0.0.0:31415")
        }
        else
        {
            UdpSocket::bind("0.0.0.0:31416")
        }
        {
            Err(why) => panic!("Cannot create socket: {}", why),
            Ok(s) => s,
        };
        if _client
        {
            println!("Waiting for bootstrap packet");
            let mut bootstrap_packet: [u8; 1 << 16] = [0; 1 << 16];
            let mut bootstrap_packet_size: usize;
            let mut send_request: Vec<u8> = Vec::new();
            let mut buffer: [u8; 128] = [0; 128];
            // Magic number
            BigEndian::write_u32(&mut buffer[0..4], MAGIC_NUMBER);
            send_request.extend_from_slice(&buffer[0..4]);
            // Protocol version
            BigEndian::write_u16(&mut buffer[0..2], PROTOCOL_VERSION);
            send_request.extend_from_slice(&buffer[0..2]);
            // Action
            send_request.push(ACTION_REQUEST_DESCRIPTION);
            let _ = match sock.set_read_timeout(Some(Duration::from_millis(1500)))
            {
                Err(why) => panic!("Cannot set read timeout: {}", why),
                Ok(_) => 0,
            };
            loop
            {
                println!("Requesting bootstrap...");
                let _ = match sock.send_to(send_request.as_slice(), &_send_address[0])
                {
                    Err(why) => panic!("Cannot send request for description: {}", why),
                    Ok(n) => n,
                };
                bootstrap_packet_size = match sock.recv_from(&mut bootstrap_packet)
                {
                    Err(_) => 0,
                    Ok(v) => v.0,
                };
                if bootstrap_packet_size < 7
                {
                    continue;
                }
                if BigEndian::read_u32(&bootstrap_packet[0..4]) != MAGIC_NUMBER
                {
                    continue;
                }
                if BigEndian::read_u16(&bootstrap_packet[4..6]) != PROTOCOL_VERSION
                {
                    println!("Protocol version mismatch");
                    continue;
                }
                if bootstrap_packet[6] != ACTION_SEND_DESCRIPTION
                {
                    continue;
                }
                println!("Bootstrapping started...");
                break;
            }
            let _ = match sock.set_read_timeout(Some(Duration::from_millis(20)))
            {
                Err(why) => panic!("Cannot set read timeout: {}", why),
                Ok(_) => 0,
            };
            let mut vec_boostrap = Vec::new();
            vec_boostrap.extend_from_slice(&bootstrap_packet[7..bootstrap_packet_size]);
            return Network {
                socket: sock,
                client: true,
                send_address: _send_address,
                pool: Pool::new(files, Some(vec_boostrap)),
                megabit_per_second: _megabit_per_second,
            };
        }
        else
        {
            let _ = match sock.set_broadcast(true)
            {
                Err(why) => panic!("Cannot enable broadcast transmission: {}", why),
                Ok(_) => 0,
            };
            return Network {
                socket: sock,
                client: false,
                send_address: _send_address,
                pool: Pool::new(files, None),
                megabit_per_second: _megabit_per_second,
            };
        }
    }
}
<commit_msg>Fixed stupid bug<commit_after>extern crate byteorder;

use commons::HASH_SIZE;
use pool::{ACTION_REQUEST_DATA, ACTION_REQUEST_DESCRIPTION, ACTION_SEND_DATA, ACTION_SEND_DESCRIPTION, MAGIC_NUMBER,
           PROTOCOL_VERSION};
use pool::Pool;
use self::byteorder::{BigEndian, ByteOrder};
use std::cmp::max;
use std::net::SocketAddr;
use std::net::UdpSocket;
use std::path::PathBuf;
use std::thread::sleep;
use std::time::{Duration, Instant};

pub struct Network
{
    socket: UdpSocket,
    client: bool,
    send_address: Vec<SocketAddr>,
    pool: Pool,
    megabit_per_second: f64,
}

impl Network
{
    fn sanity_check(&self, header: &[u8; 6]) -> bool
    {
        let mut buffer: [u8; 6] = [0; 6];
        BigEndian::write_u32(&mut buffer[0..4], MAGIC_NUMBER);
        BigEndian::write_u16(&mut buffer[4..6], PROTOCOL_VERSION);
        return header.clone() == buffer;
    }

    pub fn run(&mut self)
    {
        println!("Program started!");
        if self.client
        {
            let mut finished = false;
            'outer: while !finished
            {
                let mut started = Instant::now();
                while started.elapsed() < Duration::from_millis(2500)
                {
                    let mut buffer: [u8; 1 << 16] = [0; 1 << 16];
                    let buffer_size = match self.socket.recv_from(&mut buffer)
                    {
                        Err(_) => 0,
                        Ok(n) => n.0,
                    };
                    if buffer_size < 7
                    {
                        continue;
                    }
                    let mut head: [u8; 6] = [0; 6];
                    head.clone_from_slice(&buffer[0..6]);
                    if !self.sanity_check(&head)
                    {
                        continue;
                    }
                    if buffer[6] != ACTION_SEND_DESCRIPTION
                    {
                        continue;
                    }
                    let mut v = Vec::new();
                    v.extend_from_slice(&buffer[7..buffer_size]);
                    finished = self.pool.process_binary_description(&v);
                    if finished
                    {
                        break 'outer;
                    }
                    started = Instant::now();
                    println!("Asking for files description: {} left", self.pool.desc_left());
                }
                let mut send_request: Vec<u8> = Vec::new();
                let mut buffer: [u8; 128] = [0; 128];
                // Magic number
                BigEndian::write_u32(&mut buffer[0..4], MAGIC_NUMBER);
                send_request.extend_from_slice(&buffer[0..4]);
                // Protocol version
                BigEndian::write_u16(&mut buffer[0..2], PROTOCOL_VERSION);
                send_request.extend_from_slice(&buffer[0..2]);
                // Action
                send_request.push(ACTION_REQUEST_DESCRIPTION);
                let _ = match self.socket.send_to(send_request.as_slice(), &self.send_address[0])
                {
                    Err(why) => panic!("Cannot send request for description: {}", why),
                    Ok(n) => n,
                };
            }
        }
        let mut last_print = Instant::now();
        while self.client
        {
            if self.pool.is_complete()
            {
                return;
            }
            let mut buffer: [u8; 1 << 16] = [0; 1 << 16];
            let mut buffer_size;
            let mut count = 0;
            loop
            {
                if last_print.elapsed().as_secs() > 1
                {
                    last_print = Instant::now();
                    println!("~{} kibibytes left", self.pool.chunks_left() * 60000 / 1024);
                }
                if count > 40000 / HASH_SIZE
                {
                    break;
                }
                // TODO: if someone flood us with garbage this cycle never ends
                buffer_size = match self.socket.recv_from(&mut buffer)
                {
                    Err(_) => 0,
                    Ok(n) => n.0,
                };
                if buffer_size == 0
                {
                    break;
                }
                if buffer_size < 7
                {
                    continue;
                }
                let mut head: [u8; 6] = [0; 6];
                head.clone_from_slice(&buffer[0..6]);
                if !self.sanity_check(&head)
                {
                    continue;
                }
                if buffer[6] == ACTION_SEND_DATA
                {
                    count += 1;
                    let mut v: Vec<u8> = Vec::new();
                    v.extend_from_slice(&buffer[7..buffer_size]);
                    self.pool.process_binary_data(&v);
                    if self.pool.is_complete()
                    {
                        return;
                    }
                }
            }
            let send_req = self.pool.get_binary_chunk_request();
            let _ = self.socket.send_to(send_req.as_slice(), &self.send_address[0]);
        }
        let mut send_started = Instant::now();
        while !self.client
        {
            if last_print.elapsed().as_secs() > 1
            {
                last_print = Instant::now();
                println!("Queue length: {}", self.pool.get_queue_size());
            }
            let packet = self.pool.get_binary_send_packet();
            if packet.is_none()
            {
                let _ = self.socket.set_nonblocking(false);
            }
            else
            {
                let unwrapped_packet = packet.unwrap();
                for addr in &self.send_address
                {
                    let _ = self.socket.send_to(unwrapped_packet.as_slice(), addr);
                }
                let converted_size: f64 = (unwrapped_packet.len() as f64) * 8.0 / 1000000.0;
                let milli_stop: u64 = (converted_size * 1000.0 / self.megabit_per_second) as u64;
                let _ = self.socket.set_nonblocking(true);
                sleep(Duration::from_millis(milli_stop));
            }
            let mut buffer: [u8; 1 << 16] = [0; 1 << 16];
            let buffer_size = match self.socket.recv_from(&mut buffer)
            {
                Err(_) => 0,
                Ok(n) => n.0,
            };
            if buffer_size < 7
            {
                continue;
            }
            let mut head: [u8; 6] = [0; 6];
            head.clone_from_slice(&buffer[0..6]);
            if !self.sanity_check(&head)
            {
                continue;
            }
            if buffer[6] == ACTION_REQUEST_DATA
            {
                let mut v: Vec<u8> = Vec::new();
                v.extend_from_slice(&buffer[7..buffer_size]);
                self.pool.process_binary_chunk_request(&v);
            }
            if buffer[6] == ACTION_REQUEST_DESCRIPTION || send_started.elapsed().as_secs() >= 10
            {
                send_started = Instant::now();
                let b = self.pool.generate_binary_description();
                for bv in &b
                {
                    for addr in &self.send_address
                    {
                        let _ = self.socket.send_to(bv.as_slice(), addr);
                    }
                    sleep(max(Duration::from_millis((480.0 / self.megabit_per_second) as u64), Duration::from_millis(1)));
                }
            }
        }
    }

    pub fn new(files: &Vec<PathBuf>, _client: bool, _send_address: Vec<SocketAddr>, _megabit_per_second: f64) -> Network
    {
        println!("Welcome to Stardust");
        if _client && _send_address.len() != 1
        {
            panic!("Cannot have multiple server addresses");
        }
        let sock = match if _client
        {
            UdpSocket::bind("0.0.0.0:31415")
        }
        else
        {
            UdpSocket::bind("0.0.0.0:31416")
        }
        {
            Err(why) => panic!("Cannot create socket: {}", why),
            Ok(s) => s,
        };
        if _client
        {
            println!("Waiting for bootstrap packet");
            let mut bootstrap_packet: [u8; 1 << 16] = [0; 1 << 16];
            let mut bootstrap_packet_size: usize;
            let mut send_request: Vec<u8> = Vec::new();
            let mut buffer: [u8; 128] = [0; 128];
            // Magic number
            BigEndian::write_u32(&mut buffer[0..4], MAGIC_NUMBER);
            send_request.extend_from_slice(&buffer[0..4]);
            // Protocol version
            BigEndian::write_u16(&mut buffer[0..2], PROTOCOL_VERSION);
            send_request.extend_from_slice(&buffer[0..2]);
            // Action
            send_request.push(ACTION_REQUEST_DESCRIPTION);
            let _ = match sock.set_read_timeout(Some(Duration::from_millis(1500)))
            {
                Err(why) => panic!("Cannot set read timeout: {}", why),
                Ok(_) => 0,
            };
            loop
            {
                println!("Requesting bootstrap...");
                let _ = match sock.send_to(send_request.as_slice(), &_send_address[0])
                {
                    Err(why) => panic!("Cannot send request for description: {}", why),
                    Ok(n) => n,
                };
                bootstrap_packet_size = match sock.recv_from(&mut bootstrap_packet)
                {
                    Err(_) => 0,
                    Ok(v) => v.0,
                };
                if bootstrap_packet_size < 7
                {
                    continue;
                }
                if BigEndian::read_u32(&bootstrap_packet[0..4]) != MAGIC_NUMBER
                {
                    continue;
                }
                if BigEndian::read_u16(&bootstrap_packet[4..6]) != PROTOCOL_VERSION
                {
                    println!("Protocol version mismatch");
                    continue;
                }
                if bootstrap_packet[6] != ACTION_SEND_DESCRIPTION
                {
                    continue;
                }
                println!("Bootstrapping started...");
                break;
            }
            let _ = match sock.set_read_timeout(Some(Duration::from_millis(20)))
            {
                Err(why) => panic!("Cannot set read timeout: {}", why),
                Ok(_) => 0,
            };
            let mut vec_boostrap = Vec::new();
            vec_boostrap.extend_from_slice(&bootstrap_packet[7..bootstrap_packet_size]);
            return Network {
                socket: sock,
                client: true,
                send_address: _send_address,
                pool: Pool::new(files, Some(vec_boostrap)),
                megabit_per_second: _megabit_per_second,
            };
        }
        else
        {
            let _ = match sock.set_broadcast(true)
            {
                Err(why) => panic!("Cannot enable broadcast transmission: {}", why),
                Ok(_) => 0,
            };
            return Network {
                socket: sock,
                client: false,
                send_address: _send_address,
                pool: Pool::new(files, None),
                megabit_per_second: _megabit_per_second,
            };
        }
    }
}
<|endoftext|>


<commit_before>use std::cmp;
use std::io;

use memchr::{memchr, memrchr};
use regex::bytes::{Regex, RegexBuilder};
use syntax;

use literals::LiteralSets;
use nonl;
use {Error, Result};

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Match {
    start: usize,
    end: usize,
    line: Option<usize>,
    locations: Vec<(usize, usize)>,
}

impl Match {
    pub fn new() -> Match {
        Match::default()
    }

    /// Return the starting byte offset of the line that matched.
    #[inline]
    pub fn start(&self) -> usize {
        self.start
    }

    /// Return the ending byte offset of the line that matched.
    #[inline]
    pub fn end(&self) -> usize {
        self.end
    }

    /// Return the line number that this match corresponds to.
    ///
    /// Note that this is `None` if line numbers aren't being computed. Line
    /// number tracking can be enabled using `GrepBuilder`.
    #[inline]
    pub fn line(&self) -> Option<usize> {
        self.line
    }

    /// Return the exact start and end locations (in byte offsets) of every
    /// regex match in this line.
    ///
    /// Note that this always returns an empty slice if exact locations aren't
    /// computed. Exact location tracking can be enabled using `GrepBuilder`.
    #[inline]
    pub fn locations(&self) -> &[(usize, usize)] {
        &self.locations
    }
}

#[derive(Clone, Debug)]
pub struct Grep {
    re: Regex,
    required: Option<Regex>,
    opts: Options,
}

#[derive(Clone, Debug)]
pub struct GrepBuilder {
    pattern: String,
    opts: Options,
}

#[derive(Clone, Debug)]
struct Options {
    case_insensitive: bool,
    lines: bool,
    locations: bool,
    line_terminator: u8,
    size_limit: usize,
    dfa_size_limit: usize,
}

impl Default for Options {
    fn default() -> Options {
        Options {
            case_insensitive: false,
            lines: false,
            locations: false,
            line_terminator: b'\n',
            size_limit: 10 * (1 << 20),
            dfa_size_limit: 10 * (1 << 20),
        }
    }
}

impl GrepBuilder {
    /// Create a new builder for line searching.
    ///
    /// The pattern given should be a regular expression. The precise syntax
    /// supported is documented on the regex crate.
    pub fn new(pattern: &str) -> GrepBuilder {
        GrepBuilder {
            pattern: pattern.to_string(),
            opts: Options::default(),
        }
    }

    /// Sets whether line numbers are reported for each match.
    ///
    /// When enabled (disabled by default), every matching line is tagged with
    /// its corresponding line number accoring to the line terminator that is
    /// set. Note that this requires extra processing which can slow down
    /// search.
    pub fn line_numbers(mut self, yes: bool) -> GrepBuilder {
        self.opts.lines = yes;
        self
    }

    /// Set whether precise match locations are reported for each matching
    /// line.
    ///
    /// When enabled (disabled by default), every match of the regex on each
    /// matchling line is reported via byte offsets. Note that this requires
    /// extra processing which can slow down search.
    pub fn locations(mut self, yes: bool) -> GrepBuilder {
        self.opts.locations = yes;
        self
    }

    /// Set the line terminator.
    ///
    /// The line terminator can be any ASCII character and serves to delineate
    /// the match boundaries in the text searched.
    ///
    /// This panics if `ascii_byte` is greater than `0x7F` (i.e., not ASCII).
    pub fn line_terminator(mut self, ascii_byte: u8) -> GrepBuilder {
        assert!(ascii_byte <= 0x7F);
        self.opts.line_terminator = ascii_byte;
        self
    }

    /// Set the case sensitive flag (`i`) on the regex.
    pub fn case_insensitive(mut self, yes: bool) -> GrepBuilder {
        self.opts.case_insensitive = yes;
        self
    }

    /// Set the approximate size limit of the compiled regular expression.
    ///
    /// This roughly corresponds to the number of bytes occupied by a
    /// single compiled program. If the program exceeds this number, then a
    /// compilation error is returned.
    pub fn size_limit(mut self, limit: usize) -> GrepBuilder {
        self.opts.size_limit = limit;
        self
    }

    /// Set the approximate size of the cache used by the DFA.
    ///
    /// This roughly corresponds to the number of bytes that the DFA will use
    /// while searching.
    ///
    /// Note that this is a per thread limit. There is no way to set a global
    /// limit. In particular, if a regex is used from multiple threads
    /// simulanteously, then each thread may use up to the number of bytes
    /// specified here.
    pub fn dfa_size_limit(mut self, limit: usize) -> GrepBuilder {
        self.opts.dfa_size_limit = limit;
        self
    }

    /// Create a line searcher.
    ///
    /// If there was a problem parsing or compiling the regex with the given
    /// options, then an error is returned.
    pub fn create(self) -> Result<Grep> {
        let expr = try!(self.parse());
        let literals = LiteralSets::create(&expr);
        let re = try!(
            RegexBuilder::new(&expr.to_string())
                .case_insensitive(self.opts.case_insensitive)
                .multi_line(true)
                .unicode(true)
                .size_limit(self.opts.size_limit)
                .dfa_size_limit(self.opts.dfa_size_limit)
                .compile()
        );
        Ok(Grep {
            re: re,
            required: literals.to_regex(),
            opts: self.opts,
        })
    }

    /// Parses the underlying pattern and ensures the pattern can never match
    /// the line terminator.
    fn parse(&self) -> Result<syntax::Expr> {
        let expr =
            try!(syntax::ExprBuilder::new()
                 .allow_bytes(true)
                 .unicode(true)
                 .parse(&self.pattern));
        Ok(try!(nonl::remove(expr, self.opts.line_terminator)))
    }
}

impl Grep {
    pub fn iter<'b, 's>(&'s self, buf: &'b [u8]) -> Iter<'b, 's> {
        Iter {
            searcher: self,
            buf: buf,
            start: 0,
        }
    }

    pub fn buffered_reader<'g, R: io::Read>(
        &'g self,
        buf: Buffer,
        rdr: R,
    ) -> GrepBuffered<'g, R> {
        GrepBuffered {
            grep: self,
            rdr: rdr,
            b: buf,
            pos: 0,
            start: 0,
            lastnl: 0,
            end: 0,
        }
    }

    pub fn read_match(
        &self,
        mat: &mut Match,
        buf: &[u8],
        mut start: usize,
    ) -> bool {
        if start >= buf.len() {
            return false;
        }
        if let Some(ref req) = self.required {
            while start < buf.len() {
                let e = match req.shortest_match(&buf[start..]) {
                    None => return false,
                    Some(e) => start + e,
                };
                let (prevnl, nextnl) = self.find_line(buf, e, e);
                match self.re.shortest_match(&buf[prevnl..nextnl]) {
                    None => {
                        start = nextnl + 1;
                        continue;
                    }
                    Some(_) => {
                        self.fill_match(mat, prevnl, nextnl);
                        return true;
                    }
                }
            }
            false
        } else {
            let e = match self.re.shortest_match(&buf[start..]) {
                None => return false,
                Some(e) => start + e,
            };
            let (s, e) = self.find_line(buf, e, e);
            self.fill_match(mat, s, e);
            true
        }
    }

    fn fill_match(&self, mat: &mut Match, start: usize, end: usize) {
        mat.start = start;
        mat.end = end;
    }

    fn find_line(&self, buf: &[u8], s: usize, e: usize) -> (usize, usize) {
        (self.find_line_start(buf, s), self.find_line_end(buf, e))
    }

    fn find_line_start(&self, buf: &[u8], pos: usize) -> usize {
        memrchr(self.opts.line_terminator, &buf[0..pos]).map_or(0, |i| i + 1)
    }

    fn find_line_end(&self, buf: &[u8], pos: usize) -> usize {
        memchr(self.opts.line_terminator, &buf[pos..])
            .map_or(buf.len(), |i| pos + i)
    }
}

pub struct Buffer {
    buf: Vec<u8>,
    tmp: Vec<u8>,
}

impl Buffer {
    pub fn new() -> Buffer {
        Buffer::with_capacity(16 * (1<<10))
    }

    pub fn with_capacity(cap: usize) -> Buffer {
        Buffer {
            buf: vec![0; cap],
            tmp: Vec::new(),
        }
    }
}

pub struct GrepBuffered<'g, R> {
    grep: &'g Grep,
    rdr: R,
    b: Buffer,
    pos: usize,
    start: usize,
    lastnl: usize,
    end: usize,
}

impl<'g, R: io::Read> GrepBuffered<'g, R> {
    pub fn into_buffer(self) -> Buffer {
        self.b
    }

    pub fn iter<'b>(&'b mut self) -> IterBuffered<'b, 'g, R> {
        IterBuffered { grep: self }
    }

    pub fn read_match(
        &mut self,
        mat: &mut Match,
    ) -> Result<bool> {
        loop {
            if self.start == self.lastnl {
                if !try!(self.fill()) {
                    return Ok(false);
                }
            }
            let ok = self.grep.read_match(
                mat, &self.b.buf[..self.lastnl], self.start);
            if !ok {
                self.start = self.lastnl;
                continue;
            }
            // Move start to the first possible byte of the next line.
            self.start = cmp::min(
                self.lastnl, mat.end.checked_add(1).unwrap());
            mat.start += self.pos;
            mat.end += self.pos;
            return Ok(true);
        }
    }

    fn fill(&mut self) -> Result<bool> {
        {
            // The buffer might have leftover bytes that have not been
            // searched yet. Leftovers correspond to all bytes proceding the
            // final \n in the current buffer.
            //
            // TODO(ag): Seems like we should be able to memmove from the end
            // of the buffer to the beginning, but let's do it the stupid (but
            // safe) way for now.
            let leftovers = &self.b.buf[self.lastnl..self.end];
            self.b.tmp.clear();
            self.b.tmp.resize(leftovers.len(), 0);
            self.b.tmp.copy_from_slice(leftovers);
        }
        // Move the leftovers to the beginning of our buffer.
        self.b.buf[0..self.b.tmp.len()].copy_from_slice(&self.b.tmp);
        // Fill the rest with fresh bytes.
        let nread = try!(self.rdr.read(&mut self.b.buf[self.b.tmp.len()..]));
        // Now update our various positions.
        self.pos += self.start;
        self.start = 0;
        // The end is the total number of bytes read plus whatever we had for
        // leftovers.
        self.end = self.b.tmp.len() + nread;
        // Find the last new line. All searches on this buffer will be capped
        // at this position since any proceding bytes may correspond to a
        // partial line.
        //
        // This is a little complicated because must handle the case where
        // the buffer is not full and no new line character could be found.
        // We detect this case because this could potentially be a partial
        // line. If we fill our buffer and still can't find a `\n`, then we
        // give up.
        let mut start = 0;
        let term = self.grep.opts.line_terminator;
        loop {
            match memrchr(term, &self.b.buf[start..self.end]) {
                Some(i) => {
                    self.lastnl = start + i + 1;
                    break;
                }
                None => {
                    // If we couldn't find a new line and our buffer is
                    // completely full, then this line is terribly long and we
                    // return an error.
                    if self.end == self.b.buf.len() {
                        return Err(Error::LineTooLong(self.b.buf.len()));
                    }
                    // Otherwise we try to ask for more bytes and look again.
                    let nread = try!(
                        self.rdr.read(&mut self.b.buf[self.end..]));
                    // If we got nothing than we're at EOF and we no longer
                    // need to care about leftovers.
                    if nread == 0 {
                        self.lastnl = self.end;
                        break;
                    }
                    start = self.end;
                    self.end += nread;
                }
            }
        }
        // If end is zero, then we've hit EOF and we have no leftovers.
        Ok(self.end > 0)
    }
}

pub struct Iter<'b, 's> {
    searcher: &'s Grep,
    buf: &'b [u8],
    start: usize,
}

impl<'b, 's> Iterator for Iter<'b, 's> {
    type Item = Match;

    fn next(&mut self) -> Option<Match> {
        let mut mat = Match::default();
        if !self.searcher.read_match(&mut mat, self.buf, self.start) {
            self.start = self.buf.len();
            return None;
        }
        self.start = mat.end + 1;
        Some(mat)
    }
}

pub struct IterBuffered<'b, 'g: 'b, R: 'b> {
    grep: &'b mut GrepBuffered<'g, R>,
}

impl<'b, 'g, R: io::Read> Iterator for IterBuffered<'b, 'g, R> {
    type Item = Result<Match>;

    fn next(&mut self) -> Option<Result<Match>> {
        let mut mat = Match::default();
        match self.grep.read_match(&mut mat) {
            Err(err) => Some(Err(err)),
            Ok(false) => None,
            Ok(true) => Some(Ok(mat)),
        }
    }
}

#[allow(dead_code)]
fn s(bytes: &[u8]) -> String {
    String::from_utf8(bytes.to_vec()).unwrap()
}

#[cfg(test)]
mod tests {
    #![allow(unused_imports)]

    use memchr::{memchr, memrchr};
    use regex::bytes::Regex;

    use super::{Buffer, GrepBuilder, s};

    static SHERLOCK: &'static [u8] = include_bytes!("./data/sherlock.txt");

    #[test]
    fn buffered() {
        // Find the expected number of matches and the position of the last
        // match.
        let re = Regex::new("Sherlock Holmes").unwrap();
        let ms: Vec<_> = re.find_iter(SHERLOCK).collect();
        let expected_count = ms.len();
        let (start, end) = *ms.last().unwrap();
        let start = memrchr(b'\n', &SHERLOCK[..start]).unwrap() + 1;
        let end = memchr(b'\n', &SHERLOCK[end..]).unwrap() + end;

        // Now compare it with what Grep finds.
        let g = GrepBuilder::new("Sherlock Holmes").create().unwrap();
        let mut bg = g.buffered_reader(Buffer::new(), SHERLOCK);
        let ms: Vec<_> = bg.iter().map(|r| r.unwrap()).collect();
        let m = ms.last().unwrap();
        assert_eq!(expected_count, ms.len());
        assert_eq!(start, m.start());
        assert_eq!(end, m.end());
    }
}
<commit_msg>Refactor buffered test.<commit_after>use std::cmp;
use std::io;

use memchr::{memchr, memrchr};
use regex::bytes::{Regex, RegexBuilder};
use syntax;

use literals::LiteralSets;
use nonl;
use {Error, Result};

#[derive(Clone, Debug, Default, Eq, PartialEq)]
pub struct Match {
    start: usize,
    end: usize,
    line: Option<usize>,
    locations: Vec<(usize, usize)>,
}

impl Match {
    pub fn new() -> Match {
        Match::default()
    }

    /// Return the starting byte offset of the line that matched.
    #[inline]
    pub fn start(&self) -> usize {
        self.start
    }

    /// Return the ending byte offset of the line that matched.
    #[inline]
    pub fn end(&self) -> usize {
        self.end
    }

    /// Return the line number that this match corresponds to.
    ///
    /// Note that this is `None` if line numbers aren't being computed. Line
    /// number tracking can be enabled using `GrepBuilder`.
    #[inline]
    pub fn line(&self) -> Option<usize> {
        self.line
    }

    /// Return the exact start and end locations (in byte offsets) of every
    /// regex match in this line.
    ///
    /// Note that this always returns an empty slice if exact locations aren't
    /// computed. Exact location tracking can be enabled using `GrepBuilder`.
    #[inline]
    pub fn locations(&self) -> &[(usize, usize)] {
        &self.locations
    }
}

#[derive(Clone, Debug)]
pub struct Grep {
    re: Regex,
    required: Option<Regex>,
    opts: Options,
}

#[derive(Clone, Debug)]
pub struct GrepBuilder {
    pattern: String,
    opts: Options,
}

#[derive(Clone, Debug)]
struct Options {
    case_insensitive: bool,
    lines: bool,
    locations: bool,
    line_terminator: u8,
    size_limit: usize,
    dfa_size_limit: usize,
}

impl Default for Options {
    fn default() -> Options {
        Options {
            case_insensitive: false,
            lines: false,
            locations: false,
            line_terminator: b'\n',
            size_limit: 10 * (1 << 20),
            dfa_size_limit: 10 * (1 << 20),
        }
    }
}

impl GrepBuilder {
    /// Create a new builder for line searching.
    ///
    /// The pattern given should be a regular expression. The precise syntax
    /// supported is documented on the regex crate.
    pub fn new(pattern: &str) -> GrepBuilder {
        GrepBuilder {
            pattern: pattern.to_string(),
            opts: Options::default(),
        }
    }

    /// Sets whether line numbers are reported for each match.
    ///
    /// When enabled (disabled by default), every matching line is tagged with
    /// its corresponding line number according to the line terminator that is
    /// set. Note that this requires extra processing which can slow down
    /// search.
    pub fn line_numbers(mut self, yes: bool) -> GrepBuilder {
        self.opts.lines = yes;
        self
    }

    /// Set whether precise match locations are reported for each matching
    /// line.
    ///
    /// When enabled (disabled by default), every match of the regex on each
    /// matchling line is reported via byte offsets. Note that this requires
    /// extra processing which can slow down search.
    pub fn locations(mut self, yes: bool) -> GrepBuilder {
        self.opts.locations = yes;
        self
    }

    /// Set the line terminator.
    ///
    /// The line terminator can be any ASCII character and serves to delineate
    /// the match boundaries in the text searched.
    ///
    /// This panics if `ascii_byte` is greater than `0x7F` (i.e., not ASCII).
    pub fn line_terminator(mut self, ascii_byte: u8) -> GrepBuilder {
        assert!(ascii_byte <= 0x7F);
        self.opts.line_terminator = ascii_byte;
        self
    }

    /// Set the case sensitive flag (`i`) on the regex.
    pub fn case_insensitive(mut self, yes: bool) -> GrepBuilder {
        self.opts.case_insensitive = yes;
        self
    }

    /// Set the approximate size limit of the compiled regular expression.
    ///
    /// This roughly corresponds to the number of bytes occupied by a
    /// single compiled program. If the program exceeds this number, then a
    /// compilation error is returned.
    pub fn size_limit(mut self, limit: usize) -> GrepBuilder {
        self.opts.size_limit = limit;
        self
    }

    /// Set the approximate size of the cache used by the DFA.
    ///
    /// This roughly corresponds to the number of bytes that the DFA will use
    /// while searching.
    ///
    /// Note that this is a per thread limit. There is no way to set a global
    /// limit. In particular, if a regex is used from multiple threads
    /// simulanteously, then each thread may use up to the number of bytes
    /// specified here.
    pub fn dfa_size_limit(mut self, limit: usize) -> GrepBuilder {
        self.opts.dfa_size_limit = limit;
        self
    }

    /// Create a line searcher.
    ///
    /// If there was a problem parsing or compiling the regex with the given
    /// options, then an error is returned.
    pub fn create(self) -> Result<Grep> {
        let expr = try!(self.parse());
        let literals = LiteralSets::create(&expr);
        let re = try!(
            RegexBuilder::new(&expr.to_string())
                .case_insensitive(self.opts.case_insensitive)
                .multi_line(true)
                .unicode(true)
                .size_limit(self.opts.size_limit)
                .dfa_size_limit(self.opts.dfa_size_limit)
                .compile()
        );
        Ok(Grep {
            re: re,
            required: literals.to_regex(),
            opts: self.opts,
        })
    }

    /// Parses the underlying pattern and ensures the pattern can never match
    /// the line terminator.
    fn parse(&self) -> Result<syntax::Expr> {
        let expr =
            try!(syntax::ExprBuilder::new()
                 .allow_bytes(true)
                 .unicode(true)
                 .parse(&self.pattern));
        Ok(try!(nonl::remove(expr, self.opts.line_terminator)))
    }
}

impl Grep {
    pub fn iter<'b, 's>(&'s self, buf: &'b [u8]) -> Iter<'b, 's> {
        Iter {
            searcher: self,
            buf: buf,
            start: 0,
        }
    }

    pub fn buffered_reader<'g, R: io::Read>(
        &'g self,
        buf: Buffer,
        rdr: R,
    ) -> GrepBuffered<'g, R> {
        GrepBuffered {
            grep: self,
            rdr: rdr,
            b: buf,
            pos: 0,
            start: 0,
            lastnl: 0,
            end: 0,
        }
    }

    pub fn read_match(
        &self,
        mat: &mut Match,
        buf: &[u8],
        mut start: usize,
    ) -> bool {
        if start >= buf.len() {
            return false;
        }
        if let Some(ref req) = self.required {
            while start < buf.len() {
                let e = match req.shortest_match(&buf[start..]) {
                    None => return false,
                    Some(e) => start + e,
                };
                let (prevnl, nextnl) = self.find_line(buf, e, e);
                match self.re.shortest_match(&buf[prevnl..nextnl]) {
                    None => {
                        start = nextnl + 1;
                        continue;
                    }
                    Some(_) => {
                        self.fill_match(mat, prevnl, nextnl);
                        return true;
                    }
                }
            }
            false
        } else {
            let e = match self.re.shortest_match(&buf[start..]) {
                None => return false,
                Some(e) => start + e,
            };
            let (s, e) = self.find_line(buf, e, e);
            self.fill_match(mat, s, e);
            true
        }
    }

    fn fill_match(&self, mat: &mut Match, start: usize, end: usize) {
        mat.start = start;
        mat.end = end;
    }

    fn find_line(&self, buf: &[u8], s: usize, e: usize) -> (usize, usize) {
        (self.find_line_start(buf, s), self.find_line_end(buf, e))
    }

    fn find_line_start(&self, buf: &[u8], pos: usize) -> usize {
        memrchr(self.opts.line_terminator, &buf[0..pos]).map_or(0, |i| i + 1)
    }

    fn find_line_end(&self, buf: &[u8], pos: usize) -> usize {
        memchr(self.opts.line_terminator, &buf[pos..])
            .map_or(buf.len(), |i| pos + i)
    }
}

pub struct Buffer {
    buf: Vec<u8>,
    tmp: Vec<u8>,
}

impl Buffer {
    pub fn new() -> Buffer {
        Buffer::with_capacity(16 * (1<<10))
    }

    pub fn with_capacity(cap: usize) -> Buffer {
        Buffer {
            buf: vec![0; cap],
            tmp: Vec::new(),
        }
    }
}

pub struct GrepBuffered<'g, R> {
    grep: &'g Grep,
    rdr: R,
    b: Buffer,
    pos: usize,
    start: usize,
    lastnl: usize,
    end: usize,
}

impl<'g, R: io::Read> GrepBuffered<'g, R> {
    pub fn into_buffer(self) -> Buffer {
        self.b
    }

    pub fn iter<'b>(&'b mut self) -> IterBuffered<'b, 'g, R> {
        IterBuffered { grep: self }
    }

    pub fn read_match(
        &mut self,
        mat: &mut Match,
    ) -> Result<bool> {
        loop {
            // If the starting position is equal to the end of the last search,
            // then it's time to refill the buffer for more searching.
            if self.start == self.lastnl {
                if !try!(self.fill()) {
                    return Ok(false);
                }
            }
            let ok = self.grep.read_match(
                mat, &self.b.buf[..self.lastnl], self.start);
            if !ok {
                // This causes the next iteration to refill the buffer with
                // more bytes to search.
                self.start = self.lastnl;
                continue;
            }
            // Move start to the first possible byte of the next line.
            self.start = cmp::min(
                self.lastnl, mat.end.checked_add(1).unwrap());
            mat.start += self.pos;
            mat.end += self.pos;
            return Ok(true);
        }
    }

    fn fill(&mut self) -> Result<bool> {
        {
            // The buffer might have leftover bytes that have not been
            // searched yet. Leftovers correspond to all bytes proceding the
            // final \n in the current buffer.
            //
            // TODO(ag): Seems like we should be able to memmove from the end
            // of the buffer to the beginning, but let's do it the stupid (but
            // safe) way for now.
            let leftovers = &self.b.buf[self.lastnl..self.end];
            self.b.tmp.clear();
            self.b.tmp.resize(leftovers.len(), 0);
            self.b.tmp.copy_from_slice(leftovers);
        }
        // Move the leftovers to the beginning of our buffer.
        self.b.buf[0..self.b.tmp.len()].copy_from_slice(&self.b.tmp);
        // Fill the rest with fresh bytes.
        let nread = try!(self.rdr.read(&mut self.b.buf[self.b.tmp.len()..]));
        // Now update our position in all of the bytes searched.
        self.pos += self.start;
        self.start = 0;
        // The end is the total number of bytes read plus whatever we had for
        // leftovers.
        self.end = self.b.tmp.len() + nread;
        // Find the last new line. All searches on this buffer will be capped
        // at this position since any proceding bytes may correspond to a
        // partial line.
        //
        // This is a little complicated because we must handle the case where
        // the buffer is not full and no new line character could be found.
        // We detect this case because this could potentially be a partial
        // line. If we fill our buffer and still can't find a `\n`, then we
        // give up.
        let mut start = 0;
        let term = self.grep.opts.line_terminator;
        loop {
            match memrchr(term, &self.b.buf[start..self.end]) {
                Some(i) => {
                    self.lastnl = start + i + 1;
                    break;
                }
                None => {
                    // If we couldn't find a new line and our buffer is
                    // completely full, then this line is terribly long and we
                    // return an error.
                    if self.end == self.b.buf.len() {
                        return Err(Error::LineTooLong(self.b.buf.len()));
                    }
                    // Otherwise we try to ask for more bytes and look again.
                    let nread = try!(
                        self.rdr.read(&mut self.b.buf[self.end..]));
                    // If we got nothing then we're at EOF and we no longer
                    // need to care about leftovers.
                    if nread == 0 {
                        self.lastnl = self.end;
                        break;
                    }
                    start = self.end;
                    self.end += nread;
                }
            }
        }
        // If end is zero, then we've hit EOF and we have no leftovers.
        Ok(self.end > 0)
    }
}

pub struct Iter<'b, 's> {
    searcher: &'s Grep,
    buf: &'b [u8],
    start: usize,
}

impl<'b, 's> Iterator for Iter<'b, 's> {
    type Item = Match;

    fn next(&mut self) -> Option<Match> {
        let mut mat = Match::default();
        if !self.searcher.read_match(&mut mat, self.buf, self.start) {
            self.start = self.buf.len();
            return None;
        }
        self.start = mat.end + 1;
        Some(mat)
    }
}

pub struct IterBuffered<'b, 'g: 'b, R: 'b> {
    grep: &'b mut GrepBuffered<'g, R>,
}

impl<'b, 'g, R: io::Read> Iterator for IterBuffered<'b, 'g, R> {
    type Item = Result<Match>;

    fn next(&mut self) -> Option<Result<Match>> {
        let mut mat = Match::default();
        match self.grep.read_match(&mut mat) {
            Err(err) => Some(Err(err)),
            Ok(false) => None,
            Ok(true) => Some(Ok(mat)),
        }
    }
}

#[allow(dead_code)]
fn s(bytes: &[u8]) -> String {
    String::from_utf8(bytes.to_vec()).unwrap()
}

#[cfg(test)]
mod tests {
    #![allow(unused_imports)]

    use memchr::{memchr, memrchr};
    use regex::bytes::Regex;

    use super::{Buffer, GrepBuilder, s};

    static SHERLOCK: &'static [u8] = include_bytes!("./data/sherlock.txt");

    fn find_lines(pat: &str, haystack: &[u8]) -> Vec<(usize, usize)> {
        let re = Regex::new(pat).unwrap();
        let mut lines = vec![];
        for (s, e) in re.find_iter(haystack) {
            let start = memrchr(b'\n', &haystack[..s])
                        .map_or(0, |i| i + 1);
            let end = memchr(b'\n', &haystack[e..])
                      .map_or(haystack.len(), |i| e + i);
            lines.push((start, end));
        }
        lines
    }

    fn grep_lines(pat: &str, haystack: &[u8]) -> Vec<(usize, usize)> {
        let g = GrepBuilder::new(pat).create().unwrap();
        let mut bg = g.buffered_reader(Buffer::new(), haystack);
        bg.iter().map(|r| r.unwrap()).map(|m| (m.start(), m.end())).collect()
    }

    #[test]
    fn buffered_literal() {
        let expected = find_lines("Sherlock Holmes", SHERLOCK);
        let got = grep_lines("Sherlock Holmes", SHERLOCK);
        assert_eq!(expected.len(), got.len());
        assert_eq!(expected, got);
    }
}
<|endoftext|>