<commit_before>#[macro_use]
extern crate lazy_static;

use diffus::{
    edit::{self, string},
    Diffable,
};
use isatty::{stdin_isatty, stdout_isatty};
use regex::Regex;
use std::env;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::process::exit;
use std::process::{Command, Stdio};
use std::str;

/// If more than this part of either adds or moves is highlighted,
/// we consider it to be a replacement rather than a move, and skip
/// highlighting it.
const MAX_HIGHLIGHT_PERCENTAGE: usize = 30;

const ADD: &str = "\x1b[32m"; // Green
const REMOVE: &str = "\x1b[31m"; // Red
const HUNK_HEADER: &str = "\x1b[36m"; // Cyan

const NO_EOF_NEWLINE_COLOR: &str = "\x1b[2m"; // Faint
const NO_EOF_NEWLINE_MARKER: &str = "\\ No newline at end of file";

const INVERSE_VIDEO: &str = "\x1b[7m";
const NOT_INVERSE_VIDEO: &str = "\x1b[27m";

const BOLD: &str = "\x1b[1m";

const NORMAL: &str = "\x1b[0m";

lazy_static! {
    static ref STATIC_HEADERS: Vec<(Regex, &'static str)> = vec![
        (Regex::new("^diff ").unwrap(), BOLD),
        (Regex::new("^index ").unwrap(), BOLD),
        (Regex::new("^--- ").unwrap(), BOLD),
        (Regex::new("^\\+\\+\\+ ").unwrap(), BOLD),
        (Regex::new("^@@ ").unwrap(), HUNK_HEADER),
    ];
    static ref ANSI_COLOR_REGEX: Regex = Regex::new("\x1b[^m]*m").unwrap();
}

enum LastLineKind {
    Initial,
    Add,
    Remove,
}

/// Format add and remove lines in ADD and REMOVE colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format_adds_and_removes(adds: &String, removes: &String) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for remove_line in removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, remove_line, NORMAL));
    }
    if (!removes.is_empty()) && removes.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in adds.lines() {
        lines.push(format!("{}+{}{}", ADD, add_line, NORMAL))
    }
    if (!adds.is_empty()) && adds.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[must_use]
fn format_adds_and_removes(adds: &String, removes: &String) -> Vec<String> {
    if adds.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if removes.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    // This constant is mostly made up. The Ruby version of riff had its
    // limit set to 15_000, and this is more than that because blue.
    if adds.len() + removes.len() > 25_000 {
        return simple_format_adds_and_removes(adds, removes);
    }

    // Find diffs between adds and removals
    let mut highlighted_adds = String::new();
    let mut highlighted_removes = String::new();
    let mut adds_is_inverse = false;
    let mut removes_is_inverse = false;
    let mut adds_highlight_count = 0;
    let mut removes_highlight_count = 0;
    let diff = removes.diff(&adds);
    match diff {
        edit::Edit::Copy(unchanged) => {
            highlighted_adds.push_str(unchanged);
            highlighted_removes.push_str(unchanged);
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        string::Edit::Copy(elem) => {
                            if adds_is_inverse {
                                highlighted_adds.push_str(NOT_INVERSE_VIDEO);
                            }
                            adds_is_inverse = false;

                            if removes_is_inverse {
                                highlighted_removes.push_str(NOT_INVERSE_VIDEO);
                            }
                            removes_is_inverse = false;

                            highlighted_adds.push(elem);
                            highlighted_removes.push(elem);
                        }
                        string::Edit::Insert(elem) => {
                            adds_highlight_count += 1;
                            if !adds_is_inverse {
                                highlighted_adds.push_str(INVERSE_VIDEO);
                            }
                            adds_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_adds.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                adds_is_inverse = false;
                            }
                            highlighted_adds.push(elem);
                        }
                        string::Edit::Remove(elem) => {
                            removes_highlight_count += 1;
                            if !removes_is_inverse {
                                highlighted_removes.push_str(INVERSE_VIDEO);
                            }
                            removes_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_removes.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                removes_is_inverse = false;
                            }
                            highlighted_removes.push(elem);
                        }
                    };
                })
                .for_each(drop);
        }
    }

    if (100 * adds_highlight_count) / adds.len() > MAX_HIGHLIGHT_PERCENTAGE {
        return simple_format_adds_and_removes(adds, removes);
    }
    if (100 * removes_highlight_count) / removes.len() > MAX_HIGHLIGHT_PERCENTAGE {
        return simple_format_adds_and_removes(adds, removes);
    }

    let mut lines: Vec<String> = Vec::new();
    for highlighted_remove in highlighted_removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, highlighted_remove, NORMAL));
    }
    if (!removes.is_empty()) && removes.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for highlighted_add in highlighted_adds.lines() {
        lines.push(format!("{}+{}{}", ADD, highlighted_add, NORMAL));
    }
    if (!adds.is_empty()) && adds.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header in STATIC_HEADERS.iter() {
        let re = &static_header.0;
        if re.is_match(line) {
            return static_header.1;
        }
    }

    return "";
}

fn print(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    stream.write(text.as_bytes()).unwrap();
}

fn println(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    print(stream, &text);
    print(stream, "\n");
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut adds = String::new();
    let mut removes = String::new();
    let input = BufReader::new(input);
    let output = &mut BufWriter::new(output);
    let mut last_line_kind = LastLineKind::Initial;
    for line in input.lines() {
        let line = line.unwrap();

        // Strip out incoming ANSI formatting. This enables us to highlight
        // already-colored input.
        let line = ANSI_COLOR_REGEX.replace_all(&line, "");

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in format_adds_and_removes(&adds, &removes) {
                println(output, &line);
            }
            adds.clear();
            removes.clear();

            print(output, fixed_highlight);
            print(output, &line);
            println(output, NORMAL);
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.chars().next().unwrap() == '+' {
            adds.push_str(&line[1..]);
            adds.push('\n');
            last_line_kind = LastLineKind::Add;
            continue;
        } else if !line.is_empty() && line.chars().next().unwrap() == '-' {
            removes.push_str(&line[1..]);
            removes.push('\n');
            last_line_kind = LastLineKind::Remove;
            continue;
        }

        if line == NO_EOF_NEWLINE_MARKER {
            match last_line_kind {
                LastLineKind::Add => {
                    assert!(adds.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Remove => {
                    assert!(removes.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Initial => {
                    // This block intentionally left blank
                }
            }
        }

        last_line_kind = LastLineKind::Initial;

        // Drain outstanding adds / removes
        for line in format_adds_and_removes(&adds, &removes) {
            println(output, &line);
        }
        adds.clear();
        removes.clear();

        // Print current line
        if line == NO_EOF_NEWLINE_MARKER {
            print(output, NO_EOF_NEWLINE_COLOR);
            print(output, &line);
            println(output, NORMAL);
        } else {
            println(output, &line);
        }
    }
    for line in format_adds_and_removes(&adds, &removes) {
        println(output, &line);
    }
}

/// Try paging using the named pager (`$PATH` will be searched).
///
/// Returns `true` if the pager was found, `false` otherwise.
fn try_pager(pager_name: &str) -> bool {
    let mut command = Command::new(pager_name);
    command.stdin(Stdio::piped());

    if !env::var("LESS").is_ok() {
        // Set by git when paging
        command.env("LESS", "FRX");
    }

    if !env::var("LV").is_ok() {
        // Set by git when paging
        command.env("LV", "-c");
    }

    match command.spawn() {
        Ok(mut pager) => {
            let pager_stdin = pager.stdin.as_mut().unwrap();
            highlight_diff(&mut io::stdin().lock(), pager_stdin);

            // FIXME: Report pager exit status if non-zero, together with
            // contents of pager stderr as well if possible.
            pager.wait().expect("Waiting for pager failed");

            return true;
        }
        Err(_) => {
            return false;
        }
    }
}

fn main() {
    if stdin_isatty() {
        eprintln!("Error: Expected input from a pipe");
        exit(1);
    }

    if !stdout_isatty() {
        // We're being piped, just do stdin -> stdout
        highlight_diff(&mut io::stdin().lock(), &mut io::stdout());
        return;
    }

    let pager_env_var = env::var("PAGER");
    if pager_env_var.is_ok() {
        let pager_value = pager_env_var.unwrap();
        if try_pager(&pager_value) {
            return;
        }

        // FIXME: Print warning at the end if $PAGER was set to something that
        // doesn't exist.
    }

    if try_pager("moar") {
        return;
    }

    if try_pager("less") {
        return;
    }
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"".to_string()),
            empty
        );

        // Test adds-only
        assert_eq!(
            simple_format_adds_and_removes(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + ADD + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + ADD + "+a" + NORMAL,
                "".to_string() + ADD + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + REMOVE + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + REMOVE + "-a" + NORMAL,
                "".to_string() + REMOVE + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        assert_eq!(
            format_adds_and_removes(&"[quotes]\n".to_string(), &"<quotes>\n".to_string()),
            [
                format!(
                    "{}-{}<{}quotes{}>{}{}",
                    REMOVE,
                    INVERSE_VIDEO,
                    NOT_INVERSE_VIDEO,
                    INVERSE_VIDEO,
                    NOT_INVERSE_VIDEO,
                    NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}{}",
                    ADD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NOT_INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    fn remove(text: &str) -> String {
        return format!("{}{}{}", REMOVE, text, NORMAL);
    }

    fn add(text: &str) -> String {
        return format!("{}{}{}", ADD, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            remove(&format!("-hej{}⏎", INVERSE_VIDEO)),
            add("+hej"),
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }

    #[test]
    fn test_trailing_newline_context() {
        let mut input = "+bepa\n apa\n\\ No newline at end of file\n".as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            add("+bepa"),
            " apa",
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }
}
<commit_msg>Make a constant<commit_after>#[macro_use]
extern crate lazy_static;

use diffus::{
    edit::{self, string},
    Diffable,
};
use isatty::{stdin_isatty, stdout_isatty};
use regex::Regex;
use std::env;
use std::io::{self, BufRead, BufReader, BufWriter, Write};
use std::process::exit;
use std::process::{Command, Stdio};
use std::str;

/// If more than this part of either adds or moves is highlighted,
/// we consider it to be a replacement rather than a move, and skip
/// highlighting it.
const MAX_HIGHLIGHT_PERCENTAGE: usize = 30;

/// This constant is mostly made up. The Ruby version of riff had its
/// limit set to 15_000, and this is more than that because blue.
const MAX_HIGHLIGHT_LENGTH: usize = 25_000;

const ADD: &str = "\x1b[32m"; // Green
const REMOVE: &str = "\x1b[31m"; // Red
const HUNK_HEADER: &str = "\x1b[36m"; // Cyan

const NO_EOF_NEWLINE_COLOR: &str = "\x1b[2m"; // Faint
const NO_EOF_NEWLINE_MARKER: &str = "\\ No newline at end of file";

const INVERSE_VIDEO: &str = "\x1b[7m";
const NOT_INVERSE_VIDEO: &str = "\x1b[27m";

const BOLD: &str = "\x1b[1m";

const NORMAL: &str = "\x1b[0m";

lazy_static! {
    static ref STATIC_HEADERS: Vec<(Regex, &'static str)> = vec![
        (Regex::new("^diff ").unwrap(), BOLD),
        (Regex::new("^index ").unwrap(), BOLD),
        (Regex::new("^--- ").unwrap(), BOLD),
        (Regex::new("^\\+\\+\\+ ").unwrap(), BOLD),
        (Regex::new("^@@ ").unwrap(), HUNK_HEADER),
    ];
    static ref ANSI_COLOR_REGEX: Regex = Regex::new("\x1b[^m]*m").unwrap();
}

enum LastLineKind {
    Initial,
    Add,
    Remove,
}

/// Format add and remove lines in ADD and REMOVE colors.
///
/// No intra-line refinement.
#[must_use]
fn simple_format_adds_and_removes(adds: &String, removes: &String) -> Vec<String> {
    let mut lines: Vec<String> = Vec::new();

    for remove_line in removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, remove_line, NORMAL));
    }
    if (!removes.is_empty()) && removes.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for add_line in adds.lines() {
        lines.push(format!("{}+{}{}", ADD, add_line, NORMAL))
    }
    if (!adds.is_empty()) && adds.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[must_use]
fn format_adds_and_removes(adds: &String, removes: &String) -> Vec<String> {
    if adds.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if removes.is_empty() {
        return simple_format_adds_and_removes(adds, removes);
    }

    if adds.len() + removes.len() > MAX_HIGHLIGHT_LENGTH {
        return simple_format_adds_and_removes(adds, removes);
    }

    // Find diffs between adds and removals
    let mut highlighted_adds = String::new();
    let mut highlighted_removes = String::new();
    let mut adds_is_inverse = false;
    let mut removes_is_inverse = false;
    let mut adds_highlight_count = 0;
    let mut removes_highlight_count = 0;
    let diff = removes.diff(&adds);
    match diff {
        edit::Edit::Copy(unchanged) => {
            highlighted_adds.push_str(unchanged);
            highlighted_removes.push_str(unchanged);
        }
        edit::Edit::Change(diff) => {
            diff.into_iter()
                .map(|edit| {
                    match edit {
                        string::Edit::Copy(elem) => {
                            if adds_is_inverse {
                                highlighted_adds.push_str(NOT_INVERSE_VIDEO);
                            }
                            adds_is_inverse = false;

                            if removes_is_inverse {
                                highlighted_removes.push_str(NOT_INVERSE_VIDEO);
                            }
                            removes_is_inverse = false;

                            highlighted_adds.push(elem);
                            highlighted_removes.push(elem);
                        }
                        string::Edit::Insert(elem) => {
                            adds_highlight_count += 1;
                            if !adds_is_inverse {
                                highlighted_adds.push_str(INVERSE_VIDEO);
                            }
                            adds_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_adds.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                adds_is_inverse = false;
                            }
                            highlighted_adds.push(elem);
                        }
                        string::Edit::Remove(elem) => {
                            removes_highlight_count += 1;
                            if !removes_is_inverse {
                                highlighted_removes.push_str(INVERSE_VIDEO);
                            }
                            removes_is_inverse = true;

                            if elem == '\n' {
                                // Make sure the highlighted linefeed is visible
                                highlighted_removes.push('⏎');

                                // This will be reset by the linefeed, so we need to re-inverse on the next line
                                removes_is_inverse = false;
                            }
                            highlighted_removes.push(elem);
                        }
                    };
                })
                .for_each(drop);
        }
    }

    if (100 * adds_highlight_count) / adds.len() > MAX_HIGHLIGHT_PERCENTAGE {
        return simple_format_adds_and_removes(adds, removes);
    }
    if (100 * removes_highlight_count) / removes.len() > MAX_HIGHLIGHT_PERCENTAGE {
        return simple_format_adds_and_removes(adds, removes);
    }

    let mut lines: Vec<String> = Vec::new();
    for highlighted_remove in highlighted_removes.lines() {
        lines.push(format!("{}-{}{}", REMOVE, highlighted_remove, NORMAL));
    }
    if (!removes.is_empty()) && removes.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    for highlighted_add in highlighted_adds.lines() {
        lines.push(format!("{}+{}{}", ADD, highlighted_add, NORMAL));
    }
    if (!adds.is_empty()) && adds.chars().last().unwrap() != '\n' {
        lines.push(format!(
            "{}{}{}",
            NO_EOF_NEWLINE_COLOR, NO_EOF_NEWLINE_MARKER, NORMAL
        ));
    }

    return lines;
}

#[must_use]
fn get_fixed_highlight(line: &str) -> &str {
    for static_header in STATIC_HEADERS.iter() {
        let re = &static_header.0;
        if re.is_match(line) {
            return static_header.1;
        }
    }

    return "";
}

fn print(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    stream.write(text.as_bytes()).unwrap();
}

fn println(stream: &mut BufWriter<&mut dyn Write>, text: &str) {
    print(stream, &text);
    print(stream, "\n");
}

fn highlight_diff(input: &mut dyn io::Read, output: &mut dyn io::Write) {
    let mut adds = String::new();
    let mut removes = String::new();
    let input = BufReader::new(input);
    let output = &mut BufWriter::new(output);
    let mut last_line_kind = LastLineKind::Initial;
    for line in input.lines() {
        let line = line.unwrap();

        // Strip out incoming ANSI formatting. This enables us to highlight
        // already-colored input.
        let line = ANSI_COLOR_REGEX.replace_all(&line, "");

        let fixed_highlight = get_fixed_highlight(&line);
        if !fixed_highlight.is_empty() {
            // Drain outstanding adds / removes
            for line in format_adds_and_removes(&adds, &removes) {
                println(output, &line);
            }
            adds.clear();
            removes.clear();

            print(output, fixed_highlight);
            print(output, &line);
            println(output, NORMAL);
            continue;
        }

        // Collect adds / removes
        if !line.is_empty() && line.chars().next().unwrap() == '+' {
            adds.push_str(&line[1..]);
            adds.push('\n');
            last_line_kind = LastLineKind::Add;
            continue;
        } else if !line.is_empty() && line.chars().next().unwrap() == '-' {
            removes.push_str(&line[1..]);
            removes.push('\n');
            last_line_kind = LastLineKind::Remove;
            continue;
        }

        if line == NO_EOF_NEWLINE_MARKER {
            match last_line_kind {
                LastLineKind::Add => {
                    assert!(adds.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Remove => {
                    assert!(removes.pop().unwrap() == '\n');
                    continue;
                }
                LastLineKind::Initial => {
                    // This block intentionally left blank
                }
            }
        }

        last_line_kind = LastLineKind::Initial;

        // Drain outstanding adds / removes
        for line in format_adds_and_removes(&adds, &removes) {
            println(output, &line);
        }
        adds.clear();
        removes.clear();

        // Print current line
        if line == NO_EOF_NEWLINE_MARKER {
            print(output, NO_EOF_NEWLINE_COLOR);
            print(output, &line);
            println(output, NORMAL);
        } else {
            println(output, &line);
        }
    }
    for line in format_adds_and_removes(&adds, &removes) {
        println(output, &line);
    }
}

/// Try paging using the named pager (`$PATH` will be searched).
///
/// Returns `true` if the pager was found, `false` otherwise.
fn try_pager(pager_name: &str) -> bool {
    let mut command = Command::new(pager_name);
    command.stdin(Stdio::piped());

    if !env::var("LESS").is_ok() {
        // Set by git when paging
        command.env("LESS", "FRX");
    }

    if !env::var("LV").is_ok() {
        // Set by git when paging
        command.env("LV", "-c");
    }

    match command.spawn() {
        Ok(mut pager) => {
            let pager_stdin = pager.stdin.as_mut().unwrap();
            highlight_diff(&mut io::stdin().lock(), pager_stdin);

            // FIXME: Report pager exit status if non-zero, together with
            // contents of pager stderr as well if possible.
            pager.wait().expect("Waiting for pager failed");

            return true;
        }
        Err(_) => {
            return false;
        }
    }
}

fn main() {
    if stdin_isatty() {
        eprintln!("Error: Expected input from a pipe");
        exit(1);
    }

    if !stdout_isatty() {
        // We're being piped, just do stdin -> stdout
        highlight_diff(&mut io::stdin().lock(), &mut io::stdout());
        return;
    }

    let pager_env_var = env::var("PAGER");
    if pager_env_var.is_ok() {
        let pager_value = pager_env_var.unwrap();
        if try_pager(&pager_value) {
            return;
        }

        // FIXME: Print warning at the end if $PAGER was set to something that
        // doesn't exist.
    }

    if try_pager("moar") {
        return;
    }

    if try_pager("less") {
        return;
    }
}

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

    #[cfg(test)]
    use pretty_assertions::assert_eq;

    #[test]
    fn test_simple_format_adds_and_removes() {
        let empty: Vec<String> = Vec::new();
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"".to_string()),
            empty
        );

        // Test adds-only
        assert_eq!(
            simple_format_adds_and_removes(&"a\n".to_string(), &"".to_string()),
            ["".to_string() + ADD + "+a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&"a\nb\n".to_string(), &"".to_string()),
            [
                "".to_string() + ADD + "+a" + NORMAL,
                "".to_string() + ADD + "+b" + NORMAL,
            ]
        );

        // Test removes-only
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"a\n".to_string()),
            ["".to_string() + REMOVE + "-a" + NORMAL]
        );
        assert_eq!(
            simple_format_adds_and_removes(&"".to_string(), &"a\nb\n".to_string()),
            [
                "".to_string() + REMOVE + "-a" + NORMAL,
                "".to_string() + REMOVE + "-b" + NORMAL,
            ]
        );
    }

    #[test]
    fn test_quote_change() {
        assert_eq!(
            format_adds_and_removes(&"[quotes]\n".to_string(), &"<quotes>\n".to_string()),
            [
                format!(
                    "{}-{}<{}quotes{}>{}{}",
                    REMOVE,
                    INVERSE_VIDEO,
                    NOT_INVERSE_VIDEO,
                    INVERSE_VIDEO,
                    NOT_INVERSE_VIDEO,
                    NORMAL
                ),
                format!(
                    "{}+{}[{}quotes{}]{}{}",
                    ADD, INVERSE_VIDEO, NOT_INVERSE_VIDEO, INVERSE_VIDEO, NOT_INVERSE_VIDEO, NORMAL
                ),
            ]
        )
    }

    fn remove(text: &str) -> String {
        return format!("{}{}{}", REMOVE, text, NORMAL);
    }

    fn add(text: &str) -> String {
        return format!("{}{}{}", ADD, text, NORMAL);
    }

    #[test]
    fn test_remove_trailing_newline() {
        let mut input = "-hej\n\
            +hej\n\
            \\ No newline at end of file\n\
            "
        .as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            remove(&format!("-hej{}⏎", INVERSE_VIDEO)),
            add("+hej"),
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }

    #[test]
    fn test_trailing_newline_context() {
        let mut input = "+bepa\n apa\n\\ No newline at end of file\n".as_bytes();

        let expected = format!(
            "{}\n{}\n{}\n",
            add("+bepa"),
            " apa",
            format!(
                "{}\\ No newline at end of file{}",
                NO_EOF_NEWLINE_COLOR, NORMAL
            )
        );

        let mut output: Vec<u8> = Vec::new();
        highlight_diff(&mut input, &mut output);
        assert_eq!(std::str::from_utf8(&output).unwrap(), expected);
    }
}
<|endoftext|>


<commit_before>// Copyright 2015 Jan Likar.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use cargo::core::{GitReference, SourceId};
use cargo::util::{into_url::IntoUrl, Config};

use docopt::Docopt;

use failure::bail;

use serde::Deserialize;

type Result<T> = std::result::Result<T, failure::Error>;

#[derive(Deserialize, Debug)]
pub struct Options {
    flag_verbose: Option<bool>,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_version: Option<bool>,

    flag_prefix: Option<String>,

    arg_crate: Vec<String>,
    flag_vers: Option<String>,
    flag_git: Option<String>,
    flag_branch: Option<String>,
    flag_tag: Option<String>,
    flag_rev: Option<String>,

    flag_path: Option<String>,

    flag_alt_registry: Option<String>,

    flag_registry_url: Option<String>,

    flag_local_registry: Option<String>,
}

pub const USAGE: &str = "
Clone source code of a Rust crate

Usage:
    cargo clone [options] [<crate>]...

Options:
    --prefix DIR              Directory to clone the package into

    --vers VERS               Specify a version to clone from crates.io

    --git URL                 Git URL to clone the specified crate from
    --branch BRANCH           Branch to use when cloning from git
    --tag TAG                 Tag to use when cloning from git
    --rev SHA                 Specific commit to use when cloning from git

    --path PATH               Filesystem path to local crate to clone

    --alt-registry NAME       A registry name from Cargo config to clone the specified crate from

    --registry-url URL        A registry url to clone the specified crate from

    --local-registry PATH     A local registry path to clone the specified crate from

    -h, --help                Print this message
    -V, --version             Print version information
    -v, --verbose             Use verbose output
    -q, --quiet               Less output printed to stdout
    --color WHEN              Coloring: auto, always, never
";

fn main() {
    let options: Options = Docopt::new(USAGE)
        .and_then(|d| d.version(Some(version())).deserialize())
        .unwrap_or_else(|e| e.exit());

    let mut config = Config::default().expect("Unable to get config.");

    if let Err(e) = execute(options, &mut config) {
        config.shell().error(e).unwrap();
        std::process::exit(101);
    }
}

fn version() -> String {
    format!(
        "cargo-clone {}.{}.{}{}",
        option_env!("CARGO_PKG_VERSION_MAJOR").unwrap_or("X"),
        option_env!("CARGO_PKG_VERSION_MINOR").unwrap_or("X"),
        option_env!("CARGO_PKG_VERSION_PATCH").unwrap_or("X"),
        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or("")
    )
}

pub fn execute(options: Options, config: &mut Config) -> Result<Option<()>> {
    let verbose = match options.flag_verbose {
        Some(v) => {
            if v {
                1
            } else {
                0
            }
        }
        None => 0,
    };
    config.configure(
        verbose,
        options.flag_quiet,
        &options.flag_color,
        false,
        false,
        false,
        &None,
        &[],
    )?;

    let source_id = if let Some(url) = options.flag_git {
        let url = url.into_url()?;
        let gitref = if let Some(rev) = options.flag_rev {
            GitReference::Rev(rev)
        } else if let Some(tag) = options.flag_tag {
            GitReference::Tag(tag)
        } else if let Some(branch) = options.flag_branch {
            GitReference::Branch(branch)
        } else {
            GitReference::Branch("master".to_string())
        };
        SourceId::for_git(&url, gitref)?
    } else if let Some(path) = options.flag_path {
        SourceId::for_path(&config.cwd().join(path))?
    } else if let Some(registry) = options.flag_alt_registry.as_ref() {
        SourceId::alt_registry(config, registry)?
    } else if let Some(url) = options.flag_registry_url.as_ref() {
        let url = url.into_url()?;
        SourceId::for_registry(&url)?
    } else if let Some(path) = options.flag_local_registry.as_ref() {
        SourceId::for_local_registry(&config.cwd().join(path))?
    } else if options.arg_crate.is_empty() {
        bail!(
            "must specify a crate to clone from \
             crates.io, or use --path or --git to \
             specify alternate source"
        );
    } else {
        SourceId::crates_io(config)?
    };

    let prefix = options.flag_prefix.as_ref().map(|s| &s[..]);
    let vers = options.flag_vers.as_ref().map(|s| &s[..]);
    if options.arg_crate.is_empty() {
        for item in options.arg_crate.iter() {
            cargo_clone::ops::clone(Some(&item[..]), &source_id, prefix, vers, config)?;
        }
    } else {
        cargo_clone::ops::clone(None, &source_id, prefix, vers, config)?;
    }
    Ok(None)
}
<commit_msg>Fix sense of check for crate arg.<commit_after>// Copyright 2015 Jan Likar.
//
// Licensed under the Apache License, Version 2.0 <LICENSE-APACHE or
// http://www.apache.org/licenses/LICENSE-2.0> or the MIT license
// <LICENSE-MIT or http://opensource.org/licenses/MIT>, at your
// option. This file may not be copied, modified, or distributed
// except according to those terms.

use cargo::core::{GitReference, SourceId};
use cargo::util::{into_url::IntoUrl, Config};

use docopt::Docopt;

use failure::bail;

use serde::Deserialize;

type Result<T> = std::result::Result<T, failure::Error>;

#[derive(Deserialize, Debug)]
pub struct Options {
    flag_verbose: Option<bool>,
    flag_quiet: Option<bool>,
    flag_color: Option<String>,
    flag_version: Option<bool>,

    flag_prefix: Option<String>,

    arg_crate: Vec<String>,
    flag_vers: Option<String>,
    flag_git: Option<String>,
    flag_branch: Option<String>,
    flag_tag: Option<String>,
    flag_rev: Option<String>,

    flag_path: Option<String>,

    flag_alt_registry: Option<String>,

    flag_registry_url: Option<String>,

    flag_local_registry: Option<String>,
}

pub const USAGE: &str = "
Clone source code of a Rust crate

Usage:
    cargo clone [options] [<crate>]...

Options:
    --prefix DIR              Directory to clone the package into

    --vers VERS               Specify a version to clone from crates.io

    --git URL                 Git URL to clone the specified crate from
    --branch BRANCH           Branch to use when cloning from git
    --tag TAG                 Tag to use when cloning from git
    --rev SHA                 Specific commit to use when cloning from git

    --path PATH               Filesystem path to local crate to clone

    --alt-registry NAME       A registry name from Cargo config to clone the specified crate from

    --registry-url URL        A registry url to clone the specified crate from

    --local-registry PATH     A local registry path to clone the specified crate from

    -h, --help                Print this message
    -V, --version             Print version information
    -v, --verbose             Use verbose output
    -q, --quiet               Less output printed to stdout
    --color WHEN              Coloring: auto, always, never
";

fn main() {
    let options: Options = Docopt::new(USAGE)
        .and_then(|d| d.version(Some(version())).deserialize())
        .unwrap_or_else(|e| e.exit());

    let mut config = Config::default().expect("Unable to get config.");

    if let Err(e) = execute(options, &mut config) {
        config.shell().error(e).unwrap();
        std::process::exit(101);
    }
}

fn version() -> String {
    format!(
        "cargo-clone {}.{}.{}{}",
        option_env!("CARGO_PKG_VERSION_MAJOR").unwrap_or("X"),
        option_env!("CARGO_PKG_VERSION_MINOR").unwrap_or("X"),
        option_env!("CARGO_PKG_VERSION_PATCH").unwrap_or("X"),
        option_env!("CARGO_PKG_VERSION_PRE").unwrap_or("")
    )
}

pub fn execute(options: Options, config: &mut Config) -> Result<Option<()>> {
    let verbose = match options.flag_verbose {
        Some(v) => {
            if v {
                1
            } else {
                0
            }
        }
        None => 0,
    };
    config.configure(
        verbose,
        options.flag_quiet,
        &options.flag_color,
        false,
        false,
        false,
        &None,
        &[],
    )?;

    let source_id = if let Some(url) = options.flag_git {
        let url = url.into_url()?;
        let gitref = if let Some(rev) = options.flag_rev {
            GitReference::Rev(rev)
        } else if let Some(tag) = options.flag_tag {
            GitReference::Tag(tag)
        } else if let Some(branch) = options.flag_branch {
            GitReference::Branch(branch)
        } else {
            GitReference::Branch("master".to_string())
        };
        SourceId::for_git(&url, gitref)?
    } else if let Some(path) = options.flag_path {
        SourceId::for_path(&config.cwd().join(path))?
    } else if let Some(registry) = options.flag_alt_registry.as_ref() {
        SourceId::alt_registry(config, registry)?
    } else if let Some(url) = options.flag_registry_url.as_ref() {
        let url = url.into_url()?;
        SourceId::for_registry(&url)?
    } else if let Some(path) = options.flag_local_registry.as_ref() {
        SourceId::for_local_registry(&config.cwd().join(path))?
    } else if options.arg_crate.is_empty() {
        bail!(
            "must specify a crate to clone from \
             crates.io, or use --path or --git to \
             specify alternate source"
        );
    } else {
        SourceId::crates_io(config)?
    };

    let prefix = options.flag_prefix.as_ref().map(|s| &s[..]);
    let vers = options.flag_vers.as_ref().map(|s| &s[..]);
    if !options.arg_crate.is_empty() {
        for item in options.arg_crate.iter() {
            cargo_clone::ops::clone(Some(&item[..]), &source_id, prefix, vers, config)?;
        }
    } else {
        cargo_clone::ops::clone(None, &source_id, prefix, vers, config)?;
    }
    Ok(None)
}
<|endoftext|>


<commit_before>#[allow(dead_code)]
struct Bitstream {
    value: u16,
    width: u16,
}

// 6.1 Frame syntax
#[allow(dead_code)]
fn frame(sz: u32) {
    // startBitPos = get_position()
    // uncompressed_header()
    // trailing_bits()
    // if ( header_size_in_bytes == 0) {
    //   while ( get_position() < startBitPos + 8 *sz )
    //      padding_bit # f(1)
    //   return
    // }
    // load_probs( frame_context_idx )
    // load_probs2( frame_context_idx )
    // clear_counts()
    // init_bool( header_size_in_bytes )
    // exit_bool()
    // endBitPos = get_position()
    // headerBytes = (endBitPos - startBitPos) / 8
    // decode_tiles(sz - headerBytes)
    // refresh_probs()
}

// 6.1.1 Trailing bits syntax
#[allow(dead_code)]
fn trailing_bits() {
    // while ( get_position() & 7 )
    //   zero_bit # f(1)
}

// 6.1.2 Refresh probs syntax
#[allow(dead_code)]
fn refresh_probs() {
    // if ( error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 ) {
    //   load_probs( frame_context_idx )
    //   adapt_coef_probs()
    //   if ( FrameIsIntra == 0 ) {
    //      load_probs2( frame_context_idx )
    //      adapt_noncoef_probs()
    //   }
    // }
    // if ( refresh_frame_context )
    //   save_probs( frame_context_idx )
}

// 6.2 Uncompressed header syntax
#[allow(dead_code)]
fn uncompressed_header() {
    // frame_marker     # f(2)
    // profile_low_bit  # f(1)
    // profile_high_bit # f(1)
    // Profile = (profile_high_bit << 1) + profile_low_bit
    // if (Profile == 3)
    //   reserved_zero  # f(1)
    // show_existing_frame  # f(1)
    // if ( show_existing_frame == 1) {
    //   frame_to_show_map_idx # f(3)
    //   header_size_in_bytes = 0
    //   refresh_frame_flags = 0
    //   loop_filter_level = 0
    //   return
    // }
    // LastFrameType = frame_type
    // frame_type       # f(1)
    // show_frame       # f(1)
    // error_resilient_mode # f(1)
    // if ( frame_type == KEY_FRAME ) {
    //   frame_sync_code()
    //   color_config()
    //   frame_size()
    //   render_size()
    //   refresh_frame_flags = 0xFF
    //   FrameIsIntra = 1
    // } else {
    //   if ( show_frame == 0 ) {
    //     intra_only   # f(1)
    //   } else {
    //     intra_only = 0
    //   }
    //   FrameIsIntra = intra_only
    //   if ( error_resilient_mode ) {
    //     reset_frame_context # f(2)
    //   } else {
    //     reset_frame_context = 0
    //   }
    //   if ( intra_only == 1 ) {
    //     frame_sync_code()
    //     if ( Profile > 0 ) {
    //       color_config()
    //     } else {
    //       color_space = CS_BT_601
    //       subsampling_x = 1
    //       subsampling_y = 1
    //       BitDepth = 8
    //     }
    //     refresh_frame_flags  # f(8)
    //     frame_size()
    //     render_size()
    //   } else {
    //     refresh_frame_flags  # f(8)
    //     for ( i = 0; i < 3; i++ ) {
    //       ref_frame_idx[i]                   # f(3)
    //       ref_frame_sign_bias[LAST_FRAME+i]  # f(1)
    //     }
    //     frame_size_with_refs()
    //     allow_high_precision_mv  # f(1)
    //     read_interpolation_filter()
    //   }
    // }
    // if ( error_resilient_mode == 0 ) {
    //   refresh_frame_context          # f(1)
    //   frame_parallel_decoding_mode   # f(1)
    // } else {
    //   refresh_frame_context = 0
    //   frame_parallel_decoding_mode = 1
    // }
    // frame_context_idx # f(2)
    // if ( FrameIsIntra || error_resilient_mode ) {
    //   setup_past_independence()
    //   if ( frame_type == KEY_FRAME || error_resilient_mode == 1
    //                                || reset_frame_context == 3) {
    //     for ( i = 0; i < 4; i++ ){
    //       save_probs(i)
    //     }
    //   } else if ( reset_frame_context == 2 ) {
    //     save_probs( frame_context_idx )
    //   }
    //   frame_context_idx = 0
    // }
    // loop_filter_params()
    // quantization_params()
    // segmentation_params()
    // tile_info()
    // header_size_in_bytes # f(16)
}

// 6.2.1 Frame sync syntax
#[allow(dead_code)]
fn frame_sync_code() {
    // frame_sync_byte_0    # f(8)
    // frame_sync_byte_1    # f(8)
    // frame_sync_byte_2    # f(8)
}

// 6.2.2 Color config syntax
#[allow(dead_code)]
fn color_config() {
    // if ( Profile >= 2 ) {
    //   ten_or_twelve_bit  # f(1)
    //   BitDepth = ten_or_twelve_bit ? 12 : 10
    // } else {
    //   BitDepth = 8
    // }
    // color_space  # f(3)
    // if ( color_space != CS_RGB ) {
    //   color_range    # f(1)
    //   if ( Profile == 1 || Profile == 3 ) {
    //     subsampling_x    # f(1)
    //     subsampling_y    # f(1)
    //     reserved_zero    # f(1)
    //   } else {
    //     subsampling_x = 1
    //     subsampling_y = 1
    //   }
    // } else {
    //   color_range = 1
    //   if ( Profile == 1 || Profile == 3 ) {
    //     subsampling_x = 0
    //     subsampling_y = 0
    //     reserved_zero    # f(1)
    //   }
    // }
}

// 6.2.3 Frame size syntax
#[allow(dead_code)]
fn frame_size() {
    // frame_width_minus_1  # f(16)
    // frame_height_minus_1 # f(16)
    // FrameWidth = frame_width_minus_1 + 1
    // FrameHeight = frame_height_minus_1 + 1
    // compute_image_size()
}

// 6.2.4 Render size syntax
#[allow(dead_code)]
fn render_size() {
    // render_and_frame_size_different  # f(1)
    // if ( render_and_frame_size_different == 1 ) {
    //   render_width_minus_1   # f(16)
    //   render_height_minus_1  # f(16)
    //   renderWidth = render_width_minus_1 + 1
    //   renderHeight = render_height_minus_1 + 1
    // } else {
    //   renderWidth = FrameWidth
    //   renderHeight = FrameHeight
    // }
}

// 6.2.5 Frame size with refs syntax
#[allow(dead_code)]
fn frame_size_with_refs() {
    // for ( i = 0; i < 3; i++ ) {
    //   found_ref  # f(1)
    //   if (found_ref == 1) {
    //     FrameWidth = RefFrameWidth[ ref_frame_idx[i] ]
    //     FrameHeight = RefFrameHeight[ ref_frame_idx[i] ]
    //     break
    //   }
    // }
    // if ( found_ref == 0 )
    //   frame_size()
    // else
    //   compute_image_size()
    // render_size()
}

fn main() {
    // println!("Hello, world!");
}
<commit_msg>Add skelton upto 6.2.9<commit_after>#[allow(dead_code)]
struct Bitstream {
    value: u16,
    width: u16,
}

// 6.1 Frame syntax
#[allow(dead_code)]
fn frame(sz: u32) {
    // startBitPos = get_position()
    // uncompressed_header()
    // trailing_bits()
    // if ( header_size_in_bytes == 0) {
    //   while ( get_position() < startBitPos + 8 *sz )
    //      padding_bit # f(1)
    //   return
    // }
    // load_probs( frame_context_idx )
    // load_probs2( frame_context_idx )
    // clear_counts()
    // init_bool( header_size_in_bytes )
    // exit_bool()
    // endBitPos = get_position()
    // headerBytes = (endBitPos - startBitPos) / 8
    // decode_tiles(sz - headerBytes)
    // refresh_probs()
}

// 6.1.1 Trailing bits syntax
#[allow(dead_code)]
fn trailing_bits() {
    // while ( get_position() & 7 )
    //   zero_bit # f(1)
}

// 6.1.2 Refresh probs syntax
#[allow(dead_code)]
fn refresh_probs() {
    // if ( error_resilient_mode == 0 && frame_parallel_decoding_mode == 0 ) {
    //   load_probs( frame_context_idx )
    //   adapt_coef_probs()
    //   if ( FrameIsIntra == 0 ) {
    //      load_probs2( frame_context_idx )
    //      adapt_noncoef_probs()
    //   }
    // }
    // if ( refresh_frame_context )
    //   save_probs( frame_context_idx )
}

// 6.2 Uncompressed header syntax
#[allow(dead_code)]
fn uncompressed_header() {
    // frame_marker     # f(2)
    // profile_low_bit  # f(1)
    // profile_high_bit # f(1)
    // Profile = (profile_high_bit << 1) + profile_low_bit
    // if (Profile == 3)
    //   reserved_zero  # f(1)
    // show_existing_frame  # f(1)
    // if ( show_existing_frame == 1) {
    //   frame_to_show_map_idx # f(3)
    //   header_size_in_bytes = 0
    //   refresh_frame_flags = 0
    //   loop_filter_level = 0
    //   return
    // }
    // LastFrameType = frame_type
    // frame_type       # f(1)
    // show_frame       # f(1)
    // error_resilient_mode # f(1)
    // if ( frame_type == KEY_FRAME ) {
    //   frame_sync_code()
    //   color_config()
    //   frame_size()
    //   render_size()
    //   refresh_frame_flags = 0xFF
    //   FrameIsIntra = 1
    // } else {
    //   if ( show_frame == 0 ) {
    //     intra_only   # f(1)
    //   } else {
    //     intra_only = 0
    //   }
    //   FrameIsIntra = intra_only
    //   if ( error_resilient_mode ) {
    //     reset_frame_context # f(2)
    //   } else {
    //     reset_frame_context = 0
    //   }
    //   if ( intra_only == 1 ) {
    //     frame_sync_code()
    //     if ( Profile > 0 ) {
    //       color_config()
    //     } else {
    //       color_space = CS_BT_601
    //       subsampling_x = 1
    //       subsampling_y = 1
    //       BitDepth = 8
    //     }
    //     refresh_frame_flags  # f(8)
    //     frame_size()
    //     render_size()
    //   } else {
    //     refresh_frame_flags  # f(8)
    //     for ( i = 0; i < 3; i++ ) {
    //       ref_frame_idx[i]                   # f(3)
    //       ref_frame_sign_bias[LAST_FRAME+i]  # f(1)
    //     }
    //     frame_size_with_refs()
    //     allow_high_precision_mv  # f(1)
    //     read_interpolation_filter()
    //   }
    // }
    // if ( error_resilient_mode == 0 ) {
    //   refresh_frame_context          # f(1)
    //   frame_parallel_decoding_mode   # f(1)
    // } else {
    //   refresh_frame_context = 0
    //   frame_parallel_decoding_mode = 1
    // }
    // frame_context_idx # f(2)
    // if ( FrameIsIntra || error_resilient_mode ) {
    //   setup_past_independence()
    //   if ( frame_type == KEY_FRAME || error_resilient_mode == 1
    //                                || reset_frame_context == 3) {
    //     for ( i = 0; i < 4; i++ ){
    //       save_probs(i)
    //     }
    //   } else if ( reset_frame_context == 2 ) {
    //     save_probs( frame_context_idx )
    //   }
    //   frame_context_idx = 0
    // }
    // loop_filter_params()
    // quantization_params()
    // segmentation_params()
    // tile_info()
    // header_size_in_bytes # f(16)
}

// 6.2.1 Frame sync syntax
#[allow(dead_code)]
fn frame_sync_code() {
    // frame_sync_byte_0    # f(8)
    // frame_sync_byte_1    # f(8)
    // frame_sync_byte_2    # f(8)
}

// 6.2.2 Color config syntax
#[allow(dead_code)]
fn color_config() {
    // if ( Profile >= 2 ) {
    //   ten_or_twelve_bit  # f(1)
    //   BitDepth = ten_or_twelve_bit ? 12 : 10
    // } else {
    //   BitDepth = 8
    // }
    // color_space  # f(3)
    // if ( color_space != CS_RGB ) {
    //   color_range    # f(1)
    //   if ( Profile == 1 || Profile == 3 ) {
    //     subsampling_x    # f(1)
    //     subsampling_y    # f(1)
    //     reserved_zero    # f(1)
    //   } else {
    //     subsampling_x = 1
    //     subsampling_y = 1
    //   }
    // } else {
    //   color_range = 1
    //   if ( Profile == 1 || Profile == 3 ) {
    //     subsampling_x = 0
    //     subsampling_y = 0
    //     reserved_zero    # f(1)
    //   }
    // }
}

// 6.2.3 Frame size syntax
#[allow(dead_code)]
fn frame_size() {
    // frame_width_minus_1  # f(16)
    // frame_height_minus_1 # f(16)
    // FrameWidth = frame_width_minus_1 + 1
    // FrameHeight = frame_height_minus_1 + 1
    // compute_image_size()
}

// 6.2.4 Render size syntax
#[allow(dead_code)]
fn render_size() {
    // render_and_frame_size_different  # f(1)
    // if ( render_and_frame_size_different == 1 ) {
    //   render_width_minus_1   # f(16)
    //   render_height_minus_1  # f(16)
    //   renderWidth = render_width_minus_1 + 1
    //   renderHeight = render_height_minus_1 + 1
    // } else {
    //   renderWidth = FrameWidth
    //   renderHeight = FrameHeight
    // }
}

// 6.2.5 Frame size with refs syntax
#[allow(dead_code)]
fn frame_size_with_refs() {
    // for ( i = 0; i < 3; i++ ) {
    //   found_ref  # f(1)
    //   if (found_ref == 1) {
    //     FrameWidth = RefFrameWidth[ ref_frame_idx[i] ]
    //     FrameHeight = RefFrameHeight[ ref_frame_idx[i] ]
    //     break
    //   }
    // }
    // if ( found_ref == 0 )
    //   frame_size()
    // else
    //   compute_image_size()
    // render_size()
}

// 6.2.6 Compute image size syntax
#[allow(dead_code)]
fn compute_image_size() {
    // MiCols = (FrameWidth + 7) >> 3
    // MiRows = (FrameHeight + 7) >> 3
    // Sb64Cols = (MiCols + 7) >> 3
    // Sb64Rows = (MiRows + 7) >> 3
}

// 6.2.7 Interpolation filter syntax
#[allow(dead_code)]
fn read_interpolation_filter() {
    // is_filter_switchable         # f(1)
    // if ( is_filter_switchable == 1 ) {
    //   Interpolation_filter = SWITCHABLE
    // } else {
    //   raw_interpolation_filter   # f(2)
    //   Interpolation_filter = literal_to_type[ raw_interpolation_filter ]
    // }
}

// 6.2.8 Loop filter params syntax
#[allow(dead_code)]
fn loop_filter_params() {
    // loop_filter_level            # f(6)
    // loop_filter_sharpness        # f(3)
    // loop_filter_delta_enabled    # f(1)
    // if ( loop_filter_delta_enabled == 1 ) {
    //   loop_filter_delta_update   # f(1)
    //   if ( loop_filter_delta_update == 1 ) {
    //     for ( i = 0; i < 4; i++ ) {
    //       update_ref_delta       # f(1)
    //       if ( update_ref_delta == 1 ) {
    //         loop_filter_ref_deltas[i]    # s(6)
    //       }
    //     }
    //     for ( i = 0; i < 2; i++ ) {
    //       update_mode_delta      # f(1)
    //       if ( update_mode_delta == 1 )
    //         loop_filter_mode_deltas[i]   # s(6)
    //     }
    //   }
    // }
}

// 6.2.9 Quantization params syntax
#[allow(dead_code)]
fn quantization_params() {
    // base_q_idx   # f(8)
    // delta_q_y_dc = read_delta_q()
    // delta_q_uv_dc = read_delta_q()
    // delta_q_uv_ac = read_delta_q()
    // Lossless = base_q_idx == 0 && delta_q_y_dc == 0
    //                              && delta_q_uv_dc == 0 && delta_q_uv_ac == 0
}

fn main() {
    // println!("Hello, world!");
}
<|endoftext|>


<commit_before>#![feature(slice_patterns)]
#![feature(convert)]

extern crate yaml;

use std::io::BufReader;
use std::fs::File;
use std::fmt;

use yaml::constructor::*;
use yaml::constructor::YamlStandardData::{YamlMapping, YamlString, YamlInteger};

#[derive(Debug)]
struct Season {
    num: isize,
    matches: Vec<Match>,
}

impl Season {
    fn new(num: isize, matches: Vec<Match>) -> Season {
        Season { num: num, matches: matches }
    }
}

impl fm

#[derive(Debug)]
struct Match {
    id: isize,
    deck: String,
    opponent: String,
    result: MatchResult,
    kind: MatchType,
}

impl Match {
    fn new(id: isize) -> Match {
        Match { id: id,
                deck: String::new(),
                opponent: String::new(),
                result: MatchResult::Win,
                kind: MatchType::Casual }
    }
}

impl fmt::Display for Season {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "Season {}", self.num);

        for m in self.matches {
            writeln!(f, "\t{} vs {} {} {})", m.deck, m.opponent, m.type, m.result);
        }
    }
}

#[derive(Debug)]
enum MatchResult {
    Win,
    Loss,
}

#[derive(Debug)]
enum MatchType {
    Ranked,
    Casual,
    Friendly,
}

fn parse_seasons(map: &Vec<(YamlStandardData, YamlStandardData)>) -> Result<Vec<Season>, &'static str> {
    let mut seasons: Vec<Season> = Vec::new();

    match map.as_slice() {
        [(YamlString(ref name), YamlMapping(ref map))] => {
            let mut matches: Vec<Match>  = Vec::new();

            for m in map.as_slice() {

                match m {
                    &(YamlInteger(ref id), YamlMapping(ref content)) =>
                        matches.push(parse_match(*id, content).unwrap()),

                    _ => continue,
                }
            }

            let s_name = name.to_string();
            let temp: Vec<&str> = s_name.split(' ').collect();
            let season_num = temp[1].to_string().parse::<isize>().unwrap();

            seasons.push(Season::new(season_num, matches));
            Ok(seasons)
        },

        _ => Err("Error"),
    }

}

fn parse_match(id: isize, map: &Vec<(YamlStandardData, YamlStandardData)>) -> Result<Match, &'static str> {
    let mut m = Match::new(id);

    for e in map.as_slice() {
        match e {
            &(YamlString(ref key), YamlString(ref value)) =>

                match key.as_str() {
                    "deck"     => m.deck = value.to_string(),
                    "opponent" => m.opponent = value.to_string(),
                    "result"   =>
                        match value.as_str() {
                            "Win"  => m.result = MatchResult::Win,
                            "Loss" => m.result = MatchResult::Loss,
                            _      => continue,
                        },
                    "type"     =>
                        match value.as_str() {
                            "Ranked"   => m.kind = MatchType::Ranked,
                            "Casual"   => m.kind = MatchType::Casual,
                            "Friendly" => m.kind = MatchType::Friendly,
                            _          => continue,
                        },
                    _ => continue,
                },

            _ => continue
        }
    }

    Ok(m)
}

fn parse(data: Vec<YamlStandardData>) -> Result<Vec<Season>, &'static str> {
    for doc in data.iter() {
        match doc {
            &YamlMapping(ref map) => return parse_seasons(map),

            _ => return Err("No docs"),
        }
    }

    Err("No docs")
}

fn main() {
    let path = "/Users/Gabriele/Dropbox/hearthstone.yaml";

    let mut reader = BufReader::new(File::open(path).unwrap());
    let data = yaml::parse_io_utf8(&mut reader).unwrap();

    let seasons = parse(data);

    println!("{:?}", seasons);

}
<commit_msg>nice formatting<commit_after>#![feature(slice_patterns)]
#![feature(convert)]

extern crate yaml;

use std::io::BufReader;
use std::fs::File;
use std::fmt;

use yaml::constructor::*;
use yaml::constructor::YamlStandardData::{YamlMapping, YamlString, YamlInteger};

struct Season {
    num: isize,
    matches: Vec<Match>,
}

impl Season {
    fn new(num: isize, matches: Vec<Match>) -> Season {
        Season { num: num, matches: matches }
    }
}

struct Match {
    id: isize,
    deck: String,
    opponent: String,
    result: MatchResult,
    kind: MatchType,
}

impl Match {
    fn new(id: isize) -> Match {
        Match { id: id,
                deck: String::new(),
                opponent: String::new(),
                result: MatchResult::Win,
                kind: MatchType::Casual }
    }
}

impl fmt::Display for Season {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        writeln!(f, "Season {}", self.num);

        for m in &self.matches {
            writeln!(f, "  {} vs {} \t{} {}", m.deck, m.opponent, m.kind, m.result);
        }
        write!(f, "")
    }
}

enum MatchResult {
    Win,
    Loss,
}

impl fmt::Display for MatchResult {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match *self {
            MatchResult::Win  => "Win",
            MatchResult::Loss => "Loss",
        };

        write!(f, "{}", s)
    }
}

enum MatchType {
    Ranked,
    Casual,
    Friendly,
}

impl fmt::Display for MatchType {
    fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
        let s = match *self {
            MatchType::Ranked   => "Rankend",
            MatchType::Casual   => "Casual",
            MatchType::Friendly => "Friendly",
        };

        write!(f, "{}", s)
    }
}

fn parse_seasons(map: &Vec<(YamlStandardData, YamlStandardData)>) -> Result<Vec<Season>, &'static str> {
    let mut seasons: Vec<Season> = Vec::new();

    match map.as_slice() {
        [(YamlString(ref name), YamlMapping(ref map))] => {
            let mut matches: Vec<Match>  = Vec::new();

            for m in map.as_slice() {

                match m {
                    &(YamlInteger(ref id), YamlMapping(ref content)) =>
                        matches.push(parse_match(*id, content).unwrap()),

                    _ => continue,
                }
            }

            let s_name = name.to_string();
            let temp: Vec<&str> = s_name.split(' ').collect();
            let season_num = temp[1].to_string().parse::<isize>().unwrap();

            seasons.push(Season::new(season_num, matches));
            Ok(seasons)
        },

        _ => Err("Error"),
    }

}

fn parse_match(id: isize, map: &Vec<(YamlStandardData, YamlStandardData)>) -> Result<Match, &'static str> {
    let mut m = Match::new(id);

    for e in map.as_slice() {
        match e {
            &(YamlString(ref key), YamlString(ref value)) =>

                match key.as_str() {
                    "deck"     => m.deck = value.to_string(),
                    "opponent" => m.opponent = value.to_string(),
                    "result"   =>
                        match value.as_str() {
                            "Win"  => m.result = MatchResult::Win,
                            "Loss" => m.result = MatchResult::Loss,
                            _      => continue,
                        },
                    "type"     =>
                        match value.as_str() {
                            "Ranked"   => m.kind = MatchType::Ranked,
                            "Casual"   => m.kind = MatchType::Casual,
                            "Friendly" => m.kind = MatchType::Friendly,
                            _          => continue,
                        },
                    _ => continue,
                },

            _ => continue
        }
    }

    Ok(m)
}

fn parse(data: Vec<YamlStandardData>) -> Result<Vec<Season>, &'static str> {
    for doc in data.iter() {
        match doc {
            &YamlMapping(ref map) => return parse_seasons(map),

            _ => return Err("No docs"),
        }
    }

    Err("No docs")
}

fn main() {
    let path = "/Users/Gabriele/Dropbox/hearthstone.yaml";

    let mut reader = BufReader::new(File::open(path).unwrap());
    let data = yaml::parse_io_utf8(&mut reader).unwrap();

    let seasons = parse(data);

    for s in seasons.unwrap() {
        println!("{}", s);
    }

}
<|endoftext|>


<commit_before>extern crate clap;
extern crate walkdir;
extern crate twox_hash;

use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use clap::{App, Arg};
use walkdir::WalkDir;
use twox_hash::XxHash;
use std::hash::Hasher;

#[derive(Default)]
struct Stats {
    file_count: u64,
    total_size: u64,
    duplicate_count: u64,
}

fn collect_files(path: &str, stats: &mut Stats) -> HashMap<u64, Vec<PathBuf>> {

    let mut files = HashMap::new();

    for entry in WalkDir::new(path) {
        let entry = entry.unwrap();

        if !entry.file_type().is_file() {
            continue;
        }

        let metadata = entry.metadata().unwrap();

        if metadata.len() == 0 {
            continue;
        }

        stats.file_count += 1;
        stats.total_size += metadata.len();

        let path = entry.path().to_path_buf();

        files.entry(metadata.len()).or_insert(Vec::new()).push(path);
    }

    files
}

fn main() {
    let matches = App::new("dupr")
        .version("0.1.0")
        .author("Mike Sampson")
        .about("Duplicate file finder")
        .arg(
            Arg::with_name("DIR")
                .help("Directory to process")
                .required(true),
        )
        .arg(
            Arg::with_name("summary")
                .short("s")
                .long("summary")
                .help("Print out summary information after duplicates")
        )
        .get_matches();

    let mut stats: Stats = Default::default();

    let files = collect_files(matches.value_of("DIR").unwrap(), &mut stats);

    for f_paths in files.values().filter(|x| x.len() > 1) {

        let mut hashes = HashMap::new();
        for f_path in f_paths {
            hashes
                .entry(hash_file(&f_path))
                .or_insert(Vec::new())
                .push(f_path);
        }

        for (hash, paths) in hashes.iter().filter(|&(_, v)| v.len() > 1) {
            stats.duplicate_count += paths.len() as u64;
            for p in paths {
                println!("{:016x} - {}", hash, p.display());
            }
        }
    }

    if matches.is_present("summary")  {
        println!(
            "Processed {} files with a total size of {} bytes. {} duplicates found.",
            stats.file_count,
            stats.total_size,
            stats.duplicate_count
        );
    }
}

fn hash_file<P: AsRef<Path>>(path: P) -> u64 {
    let mut file = File::open(path).unwrap();

    let mut hash = XxHash::with_seed(0);

    let mut buffer = [0; 32 * 1024];

    while let Ok(n) = file.read(&mut buffer) {
        if n == 0 {
            break;
        }
        hash.write(&buffer[0..n]);
    }

    hash.finish()
}
<commit_msg>Tidy up. Factor out arg parsing.<commit_after>extern crate clap;
extern crate walkdir;
extern crate twox_hash;

use std::collections::HashMap;
use std::fs::File;
use std::io::Read;
use std::path::{Path, PathBuf};

use clap::{App, Arg, ArgMatches};
use walkdir::WalkDir;
use twox_hash::XxHash;
use std::hash::Hasher;

#[derive(Default)]
struct Stats {
    file_count: u64,
    total_size: u64,
    duplicate_count: u64,
}

fn parse_args<'a>() -> ArgMatches<'a> {
    App::new("dupr")
        .version("0.1.0")
        .author("Mike Sampson")
        .about("Duplicate file finder")
        .arg(
            Arg::with_name("DIR")
                .help("Directory to process")
                .required(true),
        )
        .arg(Arg::with_name("summary").short("s").long("summary").help(
            "Print out summary information after duplicates",
        ))
        .get_matches()
}

fn collect_files(path: &str, stats: &mut Stats) -> HashMap<u64, Vec<PathBuf>> {

    let mut files = HashMap::new();

    for entry in WalkDir::new(path) {
        let entry = entry.unwrap();

        if !entry.file_type().is_file() {
            continue;
        }

        let metadata = entry.metadata().unwrap();

        if metadata.len() == 0 {
            continue;
        }

        stats.file_count += 1;
        stats.total_size += metadata.len();

        let path = entry.path().to_path_buf();

        files.entry(metadata.len()).or_insert(Vec::new()).push(path);
    }

    files
}

fn main() {
    let matches = parse_args();

    let mut stats: Stats = Default::default();

    let files = collect_files(matches.value_of("DIR").unwrap(), &mut stats);

    for f_paths in files.values().filter(|x| x.len() > 1) {

        let mut hashes = HashMap::new();
        for f_path in f_paths {
            hashes
                .entry(hash_file(&f_path))
                .or_insert(Vec::new())
                .push(f_path);
        }

        for (hash, paths) in hashes.iter().filter(|&(_, v)| v.len() > 1) {
            stats.duplicate_count += paths.len() as u64;
            for p in paths {
                println!("{:016x} - {}", hash, p.display());
            }
        }
    }

    if matches.is_present("summary") {
        println!(
            "Processed {} files with a total size of {} bytes. {} duplicates found.",
            stats.file_count,
            stats.total_size,
            stats.duplicate_count
        );
    }
}

fn hash_file<P: AsRef<Path>>(path: P) -> u64 {
    let mut file = File::open(path).unwrap();

    let mut hash = XxHash::with_seed(0);

    let mut buffer = [0; 32 * 1024];

    while let Ok(n) = file.read(&mut buffer) {
        if n == 0 {
            break;
        }
        hash.write(&buffer[0..n]);
    }

    hash.finish()
}
<|endoftext|>


<commit_before>// This should fail to compile if any of these code examples are wrong.

#[allow(dead_code)]
#[allow(unused_variables)]
fn main() {
    // Doing the string conversions all in main so that the lifetimes can be inferred.

    let s = "I am a &str.";

    // &str => String
    let st: String = s.to_string();

    // String => &str
    let str: &str = st.as_slice();
}

// i32 ---------------------------------------------------------------
#[allow(dead_code)]
fn i32_to_u32(x: i32) -> u32 {
    x as u32
}

#[test]
fn test_i32_to_u32_happy() {
    let x = 3i32;
    let y = 3u32;
    assert_eq!(y, i32_to_u32(x));
}

#[allow(dead_code)]
fn i32_to_string(x: i32) -> String {
    x.to_string()
}

#[test]
fn test_i32_to_string_happy() {
    let x = 3i32;
    let y = "3";
    assert_eq!(y, i32_to_string(x));
}

#[allow(dead_code)]
fn i32_to_f64(x: i32) -> f64 {
    x as f64
}

#[test]
fn test_i32_to_f64_happy() {
    let x = 3i32;
    let y = 3.0f64;
    assert_eq!(y, i32_to_f64(x));
}

// u32 ---------------------------------------------------------------

#[allow(dead_code)]
fn u32_to_i32(x: u32) -> i32 {
    x as i32
}

#[test]
fn test_u32_to_i32_happy() {
    let x = 3u32;
    let y = 3i32;
    assert_eq!(y, u32_to_i32(x));
}

#[allow(dead_code)]
fn u32_to_string(x: u32) -> String {
    x.to_string()
}

#[test]
fn test_u32_to_string_happy() {
    let x = 3u32;
    let y = "3";
    assert_eq!(y, u32_to_string(x));
}

#[allow(dead_code)]
fn u32_to_f64(x: u32) -> f64 {
    x as f64
}

#[test]
fn test_u32_to_f64_happy() {
    let x = 3u32;
    let y = 3.0f64;
    assert_eq!(y, u32_to_f64(x));
}

// String ---------------------------------------------------------------

#[allow(dead_code)]
fn string_to_i32(x: String) -> i32 {
    x.parse().unwrap()
}

#[test]
fn test_string_to_i32_happy() {
    let x = "3".to_string();
    let y = 3i32;
    assert_eq!(y, string_to_i32(x));
}

#[allow(dead_code)]
fn string_to_u32(x: String) -> u32 {
    x.parse().unwrap()
}

#[test]
fn test_string_to_u32_happy() {
    let x = "3".to_string();
    let y = 3u32;
    assert_eq!(y, string_to_u32(x));
}

#[allow(dead_code)]
fn string_to_f64(x: String) -> f64 {
    x.parse().unwrap()
}

#[test]
fn test_string_to_f64_happy() {
    let x = "3.14".to_string();
    let y = 3.14f64;
    assert_eq!(y, string_to_f64(x));
}

// f64 ---------------------------------------------------------------

#[allow(dead_code)]
fn f64_to_i32(x: f64) -> i32 {
    x as i32
}

#[test]
fn test_f64_to_i32_happy() {
    let x = 3.14f64;
    let y = 3i32;
    assert_eq!(y, f64_to_i32(x));
}

#[allow(dead_code)]
fn f64_to_u32(x: f64) -> u32 {
    x as u32
}

#[test]
fn test_f64_to_u32_happy() {
    let x = 3.14f64;
    let y = 3u32;
    assert_eq!(y, f64_to_u32(x));
}

#[allow(dead_code)]
fn f64_to_string(x: f64) -> String {
    x.to_string()
}

#[test]
fn test_f64_to_string_happy() {
    let x = 3.14f64;
    let y = "3.14";
    assert_eq!(y, f64_to_string(x));
}

// Option --------------------------------------------------------

#[test]
fn test_parse_option_handling_with_default_some() {
    let x: i32 = "1".parse().unwrap_or(0);
    assert_eq!(x, 1);
}

#[test]
fn test_parse_option_handling_with_default_none() {
    let x: i32 = "not a number".parse().unwrap_or(0);
    assert_eq!(x, 0);
}

#[test]
fn test_parse_option_handling_expect_some() {
    let x: i32 = "1".parse().expect("Parsing int from string failed");
    assert_eq!(x, 1);
}

#[test]
#[should_fail(expected = "Parsing int from string failed")]
#[allow(unused_variables)]
fn test_parse_option_handling_expect_none() {
    let x: i32 = "not a number".parse().expect("Parsing int from string failed");
}

#[test]
fn test_parse_option_handling_unwrap_some() {
    let x: i32 = "1".parse().unwrap();
    assert_eq!(x, 1);
}

#[test]
#[should_fail(expected = "called `Option::unwrap()` on a `None` value")]
#[allow(unused_variables)]
fn test_parse_option_handling_unwrap_none() {
    let x: i32 = "not a number".parse().unwrap();
}

// Vectors ---------------------------------------------------------

#[test]
fn test_vec_to_slice_happy() {
    let x = vec!(1u8, 2u8, 3u8);
    static Y: &'static [u8] = &[1, 2, 3];
    assert_eq!(Y, x.as_slice());
}

#[test]
fn test_vec_to_boxedslice_happy() {
    let x = vec!(1u8, 2u8, 3u8);
    let y: Box<[u8]> = Box::new([1u8, 2, 3]);
    assert_eq!(y, x.into_boxed_slice());
}

#[test]
fn test_slice_to_vec_happy() {
    let x = &[1u8, 2, 3];
    let y = vec!(1u8, 2u8, 3u8);
    assert_eq!(y, x.to_vec());
}

#[test]
fn test_slice_to_boxedslice_happy() {
    let x = &[1u8, 2, 3];
    let y = Box::new([1u8, 2, 3]);
    assert_eq!(y, Box::new(*x));
}

#[test]
fn test_boxedslice_to_vec_happy() {
    let x: Box<[u8]> = Box::new([1u8, 2, 3]);
    let y = vec!(1u8, 2u8, 3u8);
    assert_eq!(y, x.into_vec());
}

#[test]
fn test_boxedslice_to_slice_happy() {
    let x: Box<[u8]> = Box::new([1u8, 2, 3]);
    let y = [1u8, 2, 3];
    assert_eq!(y, &*x);
}
<commit_msg>Update tests since parse now returns a Result, not an Option<commit_after>// This should fail to compile if any of these code examples are wrong.

#[allow(dead_code)]
#[allow(unused_variables)]
fn main() {
    // Doing the string conversions all in main so that the lifetimes can be inferred.

    let s = "I am a &str.";

    // &str => String
    let st: String = s.to_string();

    // String => &str
    let str: &str = st.as_slice();
}

// i32 ---------------------------------------------------------------
#[allow(dead_code)]
fn i32_to_u32(x: i32) -> u32 {
    x as u32
}

#[test]
fn test_i32_to_u32_happy() {
    let x = 3i32;
    let y = 3u32;
    assert_eq!(y, i32_to_u32(x));
}

#[allow(dead_code)]
fn i32_to_string(x: i32) -> String {
    x.to_string()
}

#[test]
fn test_i32_to_string_happy() {
    let x = 3i32;
    let y = "3";
    assert_eq!(y, i32_to_string(x));
}

#[allow(dead_code)]
fn i32_to_f64(x: i32) -> f64 {
    x as f64
}

#[test]
fn test_i32_to_f64_happy() {
    let x = 3i32;
    let y = 3.0f64;
    assert_eq!(y, i32_to_f64(x));
}

// u32 ---------------------------------------------------------------

#[allow(dead_code)]
fn u32_to_i32(x: u32) -> i32 {
    x as i32
}

#[test]
fn test_u32_to_i32_happy() {
    let x = 3u32;
    let y = 3i32;
    assert_eq!(y, u32_to_i32(x));
}

#[allow(dead_code)]
fn u32_to_string(x: u32) -> String {
    x.to_string()
}

#[test]
fn test_u32_to_string_happy() {
    let x = 3u32;
    let y = "3";
    assert_eq!(y, u32_to_string(x));
}

#[allow(dead_code)]
fn u32_to_f64(x: u32) -> f64 {
    x as f64
}

#[test]
fn test_u32_to_f64_happy() {
    let x = 3u32;
    let y = 3.0f64;
    assert_eq!(y, u32_to_f64(x));
}

// String ---------------------------------------------------------------

#[allow(dead_code)]
fn string_to_i32(x: String) -> i32 {
    x.parse().unwrap()
}

#[test]
fn test_string_to_i32_happy() {
    let x = "3".to_string();
    let y = 3i32;
    assert_eq!(y, string_to_i32(x));
}

#[allow(dead_code)]
fn string_to_u32(x: String) -> u32 {
    x.parse().unwrap()
}

#[test]
fn test_string_to_u32_happy() {
    let x = "3".to_string();
    let y = 3u32;
    assert_eq!(y, string_to_u32(x));
}

#[allow(dead_code)]
fn string_to_f64(x: String) -> f64 {
    x.parse().unwrap()
}

#[test]
fn test_string_to_f64_happy() {
    let x = "3.14".to_string();
    let y = 3.14f64;
    assert_eq!(y, string_to_f64(x));
}

// f64 ---------------------------------------------------------------

#[allow(dead_code)]
fn f64_to_i32(x: f64) -> i32 {
    x as i32
}

#[test]
fn test_f64_to_i32_happy() {
    let x = 3.14f64;
    let y = 3i32;
    assert_eq!(y, f64_to_i32(x));
}

#[allow(dead_code)]
fn f64_to_u32(x: f64) -> u32 {
    x as u32
}

#[test]
fn test_f64_to_u32_happy() {
    let x = 3.14f64;
    let y = 3u32;
    assert_eq!(y, f64_to_u32(x));
}

#[allow(dead_code)]
fn f64_to_string(x: f64) -> String {
    x.to_string()
}

#[test]
fn test_f64_to_string_happy() {
    let x = 3.14f64;
    let y = "3.14";
    assert_eq!(y, f64_to_string(x));
}

// Option --------------------------------------------------------

#[test]
fn test_option_handling_with_default_some() {
    let x = Some(1).unwrap_or(0);
    assert_eq!(x, 1);
}

#[test]
fn test_parse_option_handling_with_default_none() {
    let x = None.unwrap_or(0);
    assert_eq!(x, 0);
}

#[test]
fn test_parse_option_handling_expect_some() {
    let x = Some(1).expect("Parsing int from string failed");
    assert_eq!(x, 1);
}

#[test]
#[should_fail(expected = "Parsing int from string failed")]
#[allow(unused_variables)]
fn test_parse_option_handling_expect_none() {
    let x: i32 = None.expect("Parsing int from string failed");
}

#[test]
fn test_parse_option_handling_unwrap_some() {
    let x = Some(1).unwrap();
    assert_eq!(x, 1);
}

#[test]
#[should_fail(expected = "called `Option::unwrap()` on a `None` value")]
#[allow(unused_variables)]
fn test_parse_option_handling_unwrap_none() {
    let x: i32 = None.unwrap();
}

// Vectors ---------------------------------------------------------

#[test]
fn test_vec_to_slice_happy() {
    let x = vec!(1u8, 2u8, 3u8);
    static Y: &'static [u8] = &[1, 2, 3];
    assert_eq!(Y, x.as_slice());
}

#[test]
fn test_vec_to_boxedslice_happy() {
    let x = vec!(1u8, 2u8, 3u8);
    let y: Box<[u8]> = Box::new([1u8, 2, 3]);
    assert_eq!(y, x.into_boxed_slice());
}

#[test]
fn test_slice_to_vec_happy() {
    let x = &[1u8, 2, 3];
    let y = vec!(1u8, 2u8, 3u8);
    assert_eq!(y, x.to_vec());
}

#[test]
fn test_slice_to_boxedslice_happy() {
    let x = &[1u8, 2, 3];
    let y = Box::new([1u8, 2, 3]);
    assert_eq!(y, Box::new(*x));
}

#[test]
fn test_boxedslice_to_vec_happy() {
    let x: Box<[u8]> = Box::new([1u8, 2, 3]);
    let y = vec!(1u8, 2u8, 3u8);
    assert_eq!(y, x.into_vec());
}

#[test]
fn test_boxedslice_to_slice_happy() {
    let x: Box<[u8]> = Box::new([1u8, 2, 3]);
    let y = [1u8, 2, 3];
    assert_eq!(y, &*x);
}
<|endoftext|>


<commit_before>#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate indicatif;

#[macro_use]
extern crate lazy_static;

#[cfg(test)]
extern crate hyper;

use clap::App;
use reqwest::header::{AUTHORIZATION, USER_AGENT};
use indicatif::{ProgressBar, ProgressStyle};
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

mod github;

static GITHUB_TOKEN: &'static str = "RP_GITHUBTOKEN";
static USERAGENT: &'static str = "release-party-br";

lazy_static! {
    static ref RP_VERSION: String = {
        let yaml = load_yaml!("release-party.yml");
        let app = App::from_yaml(yaml);
        version_string(&app)
    };
}

fn main() {
    let yaml = load_yaml!("release-party.yml");
    let matches = App::from_yaml(yaml).get_matches();

    let org_url = make_org_url(&matches);
    let token = match env::var(GITHUB_TOKEN) {
        Ok(env_var) => env_var,
        Err(_) => {
            print_message_and_exit(
                &format!("{} environment variable should be set", GITHUB_TOKEN),
                -1,
            );
            unreachable!();
        }
    };
    let reqwest_client = get_reqwest_client(&token);

    let links = get_pr_links(
        &get_repos_we_care_about(&org_url, &reqwest_client),
        &reqwest_client,
        is_dryrun(&matches),
    );

    print_party_links(links);
}

fn version_string(app: &App) -> String {
    let mut version: Vec<u8> = Vec::new();
    app.write_version(&mut version)
        .expect("Should write to version vec.");
    String::from_utf8(version).expect("Version text should be utf8 text")
}

fn is_dryrun(matches: &clap::ArgMatches) -> bool {
    matches.is_present("DRYRUN")
}

fn org_is_just_org(org: &str) -> bool {
    if org.contains("https://api.github.com") {
        return false;
    }
    true
}

fn suggest_org_arg(org: &str) -> Result<String, String> {
    if org.starts_with("https://api.github.com/orgs/") && org.ends_with("/repos") {
        let suggestion = org
            .replace("https://api.github.com/orgs/", "")
            .replace("/repos", "");
        return Ok(suggestion.to_string());
    }
    Err("Can't make a suggestion".to_owned())
}

fn make_org_url(matches: &clap::ArgMatches) -> String {
    let org = matches
        .value_of("ORG")
        .expect("Please specify a github org");

    if !org_is_just_org(&org) {
        match suggest_org_arg(&org) {
            Ok(suggestion) => {
                print_message_and_exit(&format!("Try this for the org value: {}", suggestion), -1)
            }
            Err(_) => {
                print_message_and_exit(&"Please make org just the organization name.".to_string(), -1)
            }
        }
    }

    format!("https://api.github.com/orgs/{}/repos", org)
}

fn get_pr_links(
    repos: &Vec<github::GithubRepo>,
    reqwest_client: &reqwest::Client,
    dryrun: bool,
) -> Vec<Option<String>> {
    let pbar = ProgressBar::new(repos.len() as u64);
    bar.set_style(ProgressStyle::default_bar().template("[{elapsed_precise}] {bar:50.cyan/blue} {pos:>7}/{len:7} {msg}"));
    let mut pr_links: Vec<Option<String>> = repos
        .iter()
        .map(|repo| {
            pbar.inc(1);
            let i = match get_release_pr_for(&repo, reqwest_client, dryrun) {
                Some(pr_url) => Some(pr_url),
                None => None,
            };
            // update the PR body
            // pr_url will look like https://github.com/matthewkmayer/release-party-BR/pull/39
            // split by '/' and grab last chunk.
            if let Some(ref pr_url) = i {
                let pr_split = pr_url.split('/').collect::<Vec<&str>>();
                let pr_num = pr_split.last().expect("PR link malformed?");
                github::update_pr_body(repo, pr_num, reqwest_client, &RP_VERSION);
            }
            i
        })
        .collect();
    pbar.finish();
    // only keep the Some(PR_URL) items:
    pr_links.retain(|maybe_pr_link| maybe_pr_link.is_some());
    pr_links
}

fn get_reqwest_client(token: &str) -> reqwest::Client {
    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert(
        USER_AGENT,
        USERAGENT.parse().expect("useragent should be a string"),
    );
    headers.insert(
        AUTHORIZATION,
        format!("token {}", token)
            .parse()
            .expect("token should be a string"),
    );
    match reqwest::Client::builder().default_headers(headers).build() {
        Ok(new_client) => new_client,
        Err(e) => panic!("Couldn't create new reqwest client: {}", e),
    }
}

fn get_repos_we_care_about(
    github_org_url: &str,
    reqwest_client: &reqwest::Client,
) -> Vec<github::GithubRepo> {
    let mut repos = match github::get_repos_at(github_org_url, reqwest_client) {
        Ok(repos) => repos,
        Err(e) => panic!(format!("Couldn't get repos from github: {}", e)),
    };
    let repos_to_ignore = ignored_repos();

    // remove repos we don't care about:
    repos.retain(|repo| !repos_to_ignore.contains(&repo.name));

    repos
}

fn get_release_pr_for(
    repo: &github::GithubRepo,
    client: &reqwest::Client,
    dryrun: bool,
) -> Option<String> {
    match github::existing_release_pr_location(repo, client) {
        Some(url) => Some(url),
        None => {
            if !github::is_release_up_to_date_with_master(&repo.url, client) {
                if dryrun {
                    Some(format!("Dry run: {} would get a release PR.", repo.url))
                } else {
                    match github::create_release_pull_request(repo, client) {
                        Ok(pr_url) => Some(pr_url),
                        Err(_) => None,
                    }
                }
            } else {
                None
            }
        }
    }
}

fn print_party_links(pr_links: Vec<Option<String>>) {
    if !pr_links.is_empty() {
        println!("\nIt's a release party!  PRs to review and approve:");
        for link in pr_links {
            match link {
                Some(pr_link) => println!("{}", pr_link),
                None => println!("Party link is None: this shouldn't happen."),
            }
        }
    } else {
        println!("\nNo party today, all releases are done.");
    }
}

#[derive(Deserialize, Debug)]
struct IgnoredRepo {
    ignore: Option<Vec<String>>,
}

fn ignored_repos() -> Vec<String> {
    let hfi = match dirs::home_dir() {
        Some(path) => {
            if Path::new(&path).join(".ignoredrepos.toml").exists() {
                Some(Path::new(&path).join(".ignoredrepos.toml"))
            } else {
                None
            }
        },
        None => None,
    };

    let lfi = match Path::new("ignoredrepos.toml").exists() {
        true  => Some(Path::new("ignoredrepos.toml").to_path_buf()),
        false => None,
    };

    let fi = match (lfi, hfi) {
        (Some(a), _) => a,
        (None, Some(b)) => b,
        (_, _) => {println!("The ignoredrepos.toml file not found"); return Vec::new()},
    };

    let mut f = match File::open(&fi) {
        Ok(file) => file,
        Err(e) => {
            println!(
                "Couldn't load ignoredrepos.toml, not ignoring any repos. Reason: {}",
                e
            );
            return Vec::new();
        }
    };

    println!(
        "Found ignoredrepos.toml file at {:#?}",
        fi
    );

    let mut buffer = String::new();
    match f.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => {
            println!(
                "Couldn't read from ignoredrepos.toml, not ignoring any repos. Reason: {}",
                e
            );
            return Vec::new();
        }
    }

    let ignored: IgnoredRepo = match toml::from_str(&buffer) {
        Ok(ignored_repos) => ignored_repos,
        Err(e) => {
            println!(
                "Couldn't parse toml from ignoredrepos.toml, not ignoring any repos. Reason: {}",
                e
            );
            return Vec::new();
        }
    };
    match ignored.ignore {
        Some(repos_to_ignore) => repos_to_ignore,
        None => Vec::new(),
    }
}

fn print_message_and_exit(message: &str, exit_code: i32) {
    println!("{}", message);
    ::std::process::exit(exit_code);
}

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

    #[test]
    fn get_ignored_repos_happy_path() {
        let ignored_repositories = vec!["calagator".to_owned(), "moe".to_owned()];
        assert_eq!(ignored_repositories, ignored_repos());
    }

    #[test]
    fn handle_malformed_org() {
        assert_eq!(
            false,
            org_is_just_org("https://api.github.com/orgs/ORG-HERE/repos")
        );
    }

    #[test]
    fn handle_okay_org() {
        assert_eq!(true, org_is_just_org("ORG-HERE"));
    }

    #[test]
    fn suggestion_for_org_happy() {
        assert_eq!(
            "ORG-HERE",
            suggest_org_arg("https://api.github.com/orgs/ORG-HERE/repos").unwrap()
        );
    }

    #[test]
    fn suggestion_for_org_sad() {
        assert_eq!(
            true,
            suggest_org_arg("https://api.github.com/orgs/ORG-HERE/").is_err()
        );
        assert_eq!(
            true,
            suggest_org_arg("http://api.github.com/orgs/ORG-HERE/").is_err()
        );
        assert_eq!(
            true,
            suggest_org_arg("api.github.com/orgs/ORG-HERE/repos").is_err()
        );
    }
}
<commit_msg>Catch rebase straggler.<commit_after>#![cfg_attr(feature = "clippy", feature(plugin))]
#![cfg_attr(feature = "clippy", plugin(clippy))]

#[macro_use]
extern crate clap;
extern crate reqwest;
#[macro_use]
extern crate serde_derive;
extern crate toml;
extern crate indicatif;

#[macro_use]
extern crate lazy_static;

#[cfg(test)]
extern crate hyper;

use clap::App;
use reqwest::header::{AUTHORIZATION, USER_AGENT};
use indicatif::{ProgressBar, ProgressStyle};
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::path::Path;

mod github;

static GITHUB_TOKEN: &'static str = "RP_GITHUBTOKEN";
static USERAGENT: &'static str = "release-party-br";

lazy_static! {
    static ref RP_VERSION: String = {
        let yaml = load_yaml!("release-party.yml");
        let app = App::from_yaml(yaml);
        version_string(&app)
    };
}

fn main() {
    let yaml = load_yaml!("release-party.yml");
    let matches = App::from_yaml(yaml).get_matches();

    let org_url = make_org_url(&matches);
    let token = match env::var(GITHUB_TOKEN) {
        Ok(env_var) => env_var,
        Err(_) => {
            print_message_and_exit(
                &format!("{} environment variable should be set", GITHUB_TOKEN),
                -1,
            );
            unreachable!();
        }
    };
    let reqwest_client = get_reqwest_client(&token);

    let links = get_pr_links(
        &get_repos_we_care_about(&org_url, &reqwest_client),
        &reqwest_client,
        is_dryrun(&matches),
    );

    print_party_links(links);
}

fn version_string(app: &App) -> String {
    let mut version: Vec<u8> = Vec::new();
    app.write_version(&mut version)
        .expect("Should write to version vec.");
    String::from_utf8(version).expect("Version text should be utf8 text")
}

fn is_dryrun(matches: &clap::ArgMatches) -> bool {
    matches.is_present("DRYRUN")
}

fn org_is_just_org(org: &str) -> bool {
    if org.contains("https://api.github.com") {
        return false;
    }
    true
}

fn suggest_org_arg(org: &str) -> Result<String, String> {
    if org.starts_with("https://api.github.com/orgs/") && org.ends_with("/repos") {
        let suggestion = org
            .replace("https://api.github.com/orgs/", "")
            .replace("/repos", "");
        return Ok(suggestion.to_string());
    }
    Err("Can't make a suggestion".to_owned())
}

fn make_org_url(matches: &clap::ArgMatches) -> String {
    let org = matches
        .value_of("ORG")
        .expect("Please specify a github org");

    if !org_is_just_org(&org) {
        match suggest_org_arg(&org) {
            Ok(suggestion) => {
                print_message_and_exit(&format!("Try this for the org value: {}", suggestion), -1)
            }
            Err(_) => {
                print_message_and_exit(&"Please make org just the organization name.".to_string(), -1)
            }
        }
    }

    format!("https://api.github.com/orgs/{}/repos", org)
}

fn get_pr_links(
    repos: &Vec<github::GithubRepo>,
    reqwest_client: &reqwest::Client,
    dryrun: bool,
) -> Vec<Option<String>> {
    let pbar = ProgressBar::new(repos.len() as u64);
    pbar.set_style(ProgressStyle::default_bar().template("[{elapsed_precise}] {bar:50.cyan/blue} {pos:>7}/{len:7} {msg}"));
    let mut pr_links: Vec<Option<String>> = repos
        .iter()
        .map(|repo| {
            pbar.inc(1);
            let i = match get_release_pr_for(&repo, reqwest_client, dryrun) {
                Some(pr_url) => Some(pr_url),
                None => None,
            };
            // update the PR body
            // pr_url will look like https://github.com/matthewkmayer/release-party-BR/pull/39
            // split by '/' and grab last chunk.
            if let Some(ref pr_url) = i {
                let pr_split = pr_url.split('/').collect::<Vec<&str>>();
                let pr_num = pr_split.last().expect("PR link malformed?");
                github::update_pr_body(repo, pr_num, reqwest_client, &RP_VERSION);
            }
            i
        })
        .collect();
    pbar.finish();
    // only keep the Some(PR_URL) items:
    pr_links.retain(|maybe_pr_link| maybe_pr_link.is_some());
    pr_links
}

fn get_reqwest_client(token: &str) -> reqwest::Client {
    let mut headers = reqwest::header::HeaderMap::new();
    headers.insert(
        USER_AGENT,
        USERAGENT.parse().expect("useragent should be a string"),
    );
    headers.insert(
        AUTHORIZATION,
        format!("token {}", token)
            .parse()
            .expect("token should be a string"),
    );
    match reqwest::Client::builder().default_headers(headers).build() {
        Ok(new_client) => new_client,
        Err(e) => panic!("Couldn't create new reqwest client: {}", e),
    }
}

fn get_repos_we_care_about(
    github_org_url: &str,
    reqwest_client: &reqwest::Client,
) -> Vec<github::GithubRepo> {
    let mut repos = match github::get_repos_at(github_org_url, reqwest_client) {
        Ok(repos) => repos,
        Err(e) => panic!(format!("Couldn't get repos from github: {}", e)),
    };
    let repos_to_ignore = ignored_repos();

    // remove repos we don't care about:
    repos.retain(|repo| !repos_to_ignore.contains(&repo.name));

    repos
}

fn get_release_pr_for(
    repo: &github::GithubRepo,
    client: &reqwest::Client,
    dryrun: bool,
) -> Option<String> {
    match github::existing_release_pr_location(repo, client) {
        Some(url) => Some(url),
        None => {
            if !github::is_release_up_to_date_with_master(&repo.url, client) {
                if dryrun {
                    Some(format!("Dry run: {} would get a release PR.", repo.url))
                } else {
                    match github::create_release_pull_request(repo, client) {
                        Ok(pr_url) => Some(pr_url),
                        Err(_) => None,
                    }
                }
            } else {
                None
            }
        }
    }
}

fn print_party_links(pr_links: Vec<Option<String>>) {
    if !pr_links.is_empty() {
        println!("\nIt's a release party!  PRs to review and approve:");
        for link in pr_links {
            match link {
                Some(pr_link) => println!("{}", pr_link),
                None => println!("Party link is None: this shouldn't happen."),
            }
        }
    } else {
        println!("\nNo party today, all releases are done.");
    }
}

#[derive(Deserialize, Debug)]
struct IgnoredRepo {
    ignore: Option<Vec<String>>,
}

fn ignored_repos() -> Vec<String> {
    let hfi = match dirs::home_dir() {
        Some(path) => {
            if Path::new(&path).join(".ignoredrepos.toml").exists() {
                Some(Path::new(&path).join(".ignoredrepos.toml"))
            } else {
                None
            }
        },
        None => None,
    };

    let lfi = match Path::new("ignoredrepos.toml").exists() {
        true  => Some(Path::new("ignoredrepos.toml").to_path_buf()),
        false => None,
    };

    let fi = match (lfi, hfi) {
        (Some(a), _) => a,
        (None, Some(b)) => b,
        (_, _) => {println!("The ignoredrepos.toml file not found"); return Vec::new()},
    };

    let mut f = match File::open(&fi) {
        Ok(file) => file,
        Err(e) => {
            println!(
                "Couldn't load ignoredrepos.toml, not ignoring any repos. Reason: {}",
                e
            );
            return Vec::new();
        }
    };

    println!(
        "Found ignoredrepos.toml file at {:#?}",
        fi
    );

    let mut buffer = String::new();
    match f.read_to_string(&mut buffer) {
        Ok(_) => (),
        Err(e) => {
            println!(
                "Couldn't read from ignoredrepos.toml, not ignoring any repos. Reason: {}",
                e
            );
            return Vec::new();
        }
    }

    let ignored: IgnoredRepo = match toml::from_str(&buffer) {
        Ok(ignored_repos) => ignored_repos,
        Err(e) => {
            println!(
                "Couldn't parse toml from ignoredrepos.toml, not ignoring any repos. Reason: {}",
                e
            );
            return Vec::new();
        }
    };
    match ignored.ignore {
        Some(repos_to_ignore) => repos_to_ignore,
        None => Vec::new(),
    }
}

fn print_message_and_exit(message: &str, exit_code: i32) {
    println!("{}", message);
    ::std::process::exit(exit_code);
}

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

    #[test]
    fn get_ignored_repos_happy_path() {
        let ignored_repositories = vec!["calagator".to_owned(), "moe".to_owned()];
        assert_eq!(ignored_repositories, ignored_repos());
    }

    #[test]
    fn handle_malformed_org() {
        assert_eq!(
            false,
            org_is_just_org("https://api.github.com/orgs/ORG-HERE/repos")
        );
    }

    #[test]
    fn handle_okay_org() {
        assert_eq!(true, org_is_just_org("ORG-HERE"));
    }

    #[test]
    fn suggestion_for_org_happy() {
        assert_eq!(
            "ORG-HERE",
            suggest_org_arg("https://api.github.com/orgs/ORG-HERE/repos").unwrap()
        );
    }

    #[test]
    fn suggestion_for_org_sad() {
        assert_eq!(
            true,
            suggest_org_arg("https://api.github.com/orgs/ORG-HERE/").is_err()
        );
        assert_eq!(
            true,
            suggest_org_arg("http://api.github.com/orgs/ORG-HERE/").is_err()
        );
        assert_eq!(
            true,
            suggest_org_arg("api.github.com/orgs/ORG-HERE/repos").is_err()
        );
    }
}
<|endoftext|>