<commit_before>use std::collections::HashMap;

use {Error, Result};
use reader::Reader;

/// A tag.
pub enum Tag {
    Path(Type, Attributes),
    Unknown(String, Type, Attributes),
}

/// The type of a tag.
///
/// http://www.w3.org/TR/REC-xml/#sec-starttags
pub enum Type {
    Start,
    End,
    EmptyElement,
}

/// The attributes of a tag.
pub type Attributes = HashMap<String, String>;

struct Parser<'s> {
    reader: Reader<'s>,
}

impl Tag {
    /// Parse the content between a pair of angle brackets.
    pub fn parse(text: &str) -> Result<Tag> {
        Parser::new(text).process()
    }
}

macro_rules! raise(
    ($parser:expr, $($arg:tt)*) => ({
        let (line, column) = $parser.reader.position();
        return Err(Error {
            line: line,
            column: column,
            message: format!($($arg)*),
        })
    });
);

impl<'s> Parser<'s> {
    #[inline]
    fn new(text: &'s str) -> Parser<'s> {
        Parser {
            reader: Reader::new(text),
        }
    }

    fn process(&mut self) -> Result<Tag> {
        if self.reader.consume_char('/') {
            self.read_end_tag()
        } else {
            self.read_start_or_empty_element_tag()
        }
    }

    fn read_end_tag(&mut self) -> Result<Tag> {
        use std::ascii::OwnedAsciiExt;

        let name = try!(self.read_name());

        self.reader.consume_whitespace();

        if !self.reader.is_done() {
            raise!(self, "found an end tag with excessive data");
        }

        Ok(match &(name.clone().into_ascii_lowercase())[] {
            "path" => Tag::Path(Type::End, HashMap::new()),
            _ => Tag::Unknown(name, Type::End, HashMap::new()),
        })
    }

    fn read_start_or_empty_element_tag(&mut self) -> Result<Tag> {
        use std::ascii::OwnedAsciiExt;

        let name = try!(self.read_name());
        let attributes = try!(self.read_attributes());

        self.reader.consume_whitespace();

        let tail = self.reader.capture(|reader| {
            reader.consume_all();
        }).and_then(|tail| Some(String::from_str(tail)));

        let typo = match tail {
            Some(tail) => match &tail[] {
                "/" => Type::EmptyElement,
                _ => raise!(self, "found an unexpected ending of a tag"),
            },
            _ => Type::Start,
        };

        Ok(match &(name.clone().into_ascii_lowercase())[] {
            "path" => Tag::Path(typo, attributes),
            _ => Tag::Unknown(name, typo, attributes),
        })
    }

    #[inline]
    fn read_name(&mut self) -> Result<String> {
        let name = self.reader.capture(|reader| {
            reader.consume_name();
        }).and_then(|name| Some(String::from_str(name)));

        match name {
            Some(name) => Ok(name),
            None => raise!(self, "expected a name"),
        }
    }

    fn read_attributes(&mut self) -> Result<Attributes> {
        let mut attributes = HashMap::new();

        loop {
            self.reader.consume_whitespace();

            match try!(self.read_attribute()) {
                Some((name, value)) => {
                    attributes.insert(name, value);
                },
                _ => break,
            }
        }

        Ok(attributes)
    }

    fn read_attribute(&mut self) -> Result<Option<(String, String)>> {
        let attribute = self.reader.capture(|reader| {
            reader.consume_attribute();
        }).and_then(|attribute| Some(String::from_str(attribute)));

        match attribute {
            Some(attribute) => {
                let k = (&attribute).find('=').unwrap();
                let name = (&attribute[0..k]).trim_right();
                let value = (&attribute[(k+1)..]).trim_left();
                let value = &value[1..(value.len()-1)];
                Ok(Some((String::from_str(name), String::from_str(value))))
            },
            _ => Ok(None),
        }
    }
}

#[cfg(test)]
mod tests {
    use super::{Parser, Tag, Type};

    #[test]
    fn parser_process() {
        macro_rules! test(
            ($text:expr, $typo:ident) => ({
                let mut parser = Parser::new($text);
                match parser.process().unwrap() {
                    Tag::Unknown(_, Type::$typo, _) => {},
                    _ => assert!(false),
                }
            });
        );

        test!("foo", Start);
        test!("foo ", Start);
        test!("/foo", End);
        test!("/foo ", End);
        test!("foo/", EmptyElement);
        test!("foo /", EmptyElement);
    }

    #[test]
    fn parser_read_attribute() {
        macro_rules! test(
            ($text:expr, $name:expr, $value:expr) => ({
                let mut parser = Parser::new($text);
                let (name, value) = parser.read_attribute().unwrap().unwrap();
                assert_eq!(&name[], $name);
                assert_eq!(&value[], $value);
            });
        );

        test!("foo='bar'", "foo", "bar");
        test!("foo =\"bar\"", "foo", "bar");
        test!("foo= \"bar\"", "foo", "bar");
        test!("foo\t=\n'bar'  ", "foo", "bar");
    }
}
<commit_msg>Move methods around in tag::Parser<commit_after>use std::collections::HashMap;

use {Error, Result};
use reader::Reader;

/// A tag.
pub enum Tag {
    Path(Type, Attributes),
    Unknown(String, Type, Attributes),
}

/// The type of a tag.
///
/// http://www.w3.org/TR/REC-xml/#sec-starttags
pub enum Type {
    Start,
    End,
    EmptyElement,
}

/// The attributes of a tag.
pub type Attributes = HashMap<String, String>;

struct Parser<'s> {
    reader: Reader<'s>,
}

impl Tag {
    /// Parse the content between a pair of angle brackets.
    pub fn parse(text: &str) -> Result<Tag> {
        Parser::new(text).process()
    }
}

macro_rules! raise(
    ($parser:expr, $($arg:tt)*) => ({
        let (line, column) = $parser.reader.position();
        return Err(Error {
            line: line,
            column: column,
            message: format!($($arg)*),
        })
    });
);

impl<'s> Parser<'s> {
    #[inline]
    fn new(text: &'s str) -> Parser<'s> {
        Parser {
            reader: Reader::new(text),
        }
    }

    fn process(&mut self) -> Result<Tag> {
        if self.reader.consume_char('/') {
            self.read_end_tag()
        } else {
            self.read_start_or_empty_element_tag()
        }
    }

    fn read_attribute(&mut self) -> Result<Option<(String, String)>> {
        let attribute = self.reader.capture(|reader| {
            reader.consume_attribute();
        }).and_then(|attribute| Some(String::from_str(attribute)));

        match attribute {
            Some(attribute) => {
                let k = (&attribute).find('=').unwrap();
                let name = (&attribute[0..k]).trim_right();
                let value = (&attribute[(k+1)..]).trim_left();
                let value = &value[1..(value.len()-1)];
                Ok(Some((String::from_str(name), String::from_str(value))))
            },
            _ => Ok(None),
        }
    }

    fn read_attributes(&mut self) -> Result<Attributes> {
        let mut attributes = HashMap::new();

        loop {
            self.reader.consume_whitespace();

            match try!(self.read_attribute()) {
                Some((name, value)) => {
                    attributes.insert(name, value);
                },
                _ => break,
            }
        }

        Ok(attributes)
    }

    fn read_end_tag(&mut self) -> Result<Tag> {
        use std::ascii::OwnedAsciiExt;

        let name = try!(self.read_name());

        self.reader.consume_whitespace();

        if !self.reader.is_done() {
            raise!(self, "found an end tag with excessive data");
        }

        Ok(match &(name.clone().into_ascii_lowercase())[] {
            "path" => Tag::Path(Type::End, HashMap::new()),
            _ => Tag::Unknown(name, Type::End, HashMap::new()),
        })
    }

    #[inline]
    fn read_name(&mut self) -> Result<String> {
        let name = self.reader.capture(|reader| {
            reader.consume_name();
        }).and_then(|name| Some(String::from_str(name)));

        match name {
            Some(name) => Ok(name),
            None => raise!(self, "expected a name"),
        }
    }

    fn read_start_or_empty_element_tag(&mut self) -> Result<Tag> {
        use std::ascii::OwnedAsciiExt;

        let name = try!(self.read_name());
        let attributes = try!(self.read_attributes());

        self.reader.consume_whitespace();

        let tail = self.reader.capture(|reader| {
            reader.consume_all();
        }).and_then(|tail| Some(String::from_str(tail)));

        let typo = match tail {
            Some(tail) => match &tail[] {
                "/" => Type::EmptyElement,
                _ => raise!(self, "found an unexpected ending of a tag"),
            },
            _ => Type::Start,
        };

        Ok(match &(name.clone().into_ascii_lowercase())[] {
            "path" => Tag::Path(typo, attributes),
            _ => Tag::Unknown(name, typo, attributes),
        })
    }
}

#[cfg(test)]
mod tests {
    use super::{Parser, Tag, Type};

    #[test]
    fn parser_process() {
        macro_rules! test(
            ($text:expr, $typo:ident) => ({
                let mut parser = Parser::new($text);
                match parser.process().unwrap() {
                    Tag::Unknown(_, Type::$typo, _) => {},
                    _ => assert!(false),
                }
            });
        );

        test!("foo", Start);
        test!("foo ", Start);
        test!("/foo", End);
        test!("/foo ", End);
        test!("foo/", EmptyElement);
        test!("foo /", EmptyElement);
    }

    #[test]
    fn parser_read_attribute() {
        macro_rules! test(
            ($text:expr, $name:expr, $value:expr) => ({
                let mut parser = Parser::new($text);
                let (name, value) = parser.read_attribute().unwrap().unwrap();
                assert_eq!(&name[], $name);
                assert_eq!(&value[], $value);
            });
        );

        test!("foo='bar'", "foo", "bar");
        test!("foo =\"bar\"", "foo", "bar");
        test!("foo= \"bar\"", "foo", "bar");
        test!("foo\t=\n'bar'  ", "foo", "bar");
    }
}
<|endoftext|>


<commit_before>extern crate compiletest_rs as compiletest;

use std::path::PathBuf;

#[allow(dead_code)]
fn run_mode(directory: &'static str, mode: &'static str) {
    let mut config = compiletest::default_config();
    let cfg_mode = mode.parse().ok().expect("Invalid mode");

    config.mode = cfg_mode;
    config.target_rustcflags = Some("-L target/debug/".to_owned());
    config.src_base = PathBuf::from(format!("tests/{}", directory));

    compiletest::run_tests(&config);
}

// #[test]
// fn compile_test() {
//     run_mode("compile-fail", "compile-fail");
// }

#[cfg(feature = "unicase_support")]
#[test]
fn compile_test_unicase() {
    run_mode("compile-fail-unicase", "compile-fail");
}
<commit_msg>Remove dead code<commit_after>extern crate compiletest_rs as compiletest;

use std::path::PathBuf;

#[allow(dead_code)]
fn run_mode(directory: &'static str, mode: &'static str) {
    let mut config = compiletest::default_config();
    let cfg_mode = mode.parse().ok().expect("Invalid mode");

    config.mode = cfg_mode;
    config.target_rustcflags = Some("-L target/debug/".to_owned());
    config.src_base = PathBuf::from(format!("tests/{}", directory));

    compiletest::run_tests(&config);
}

#[cfg(feature = "unicase_support")]
#[test]
fn compile_test_unicase() {
    run_mode("compile-fail-unicase", "compile-fail");
}
<|endoftext|>


<commit_before>use super::Rope;
use std::iter;

#[test]
fn rebalance_test_1() {
    let r = Rope::from("This is a large string \
                        that will need to be rebalanced.".to_string());
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn rebalance_test_2() {
    let r = Rope::from("Lorem ipsum dolor sit amet, consectetur adipiscing eli\
                        t, sed do eiusmod tempor incididunt ut labore et dolor\
                        e magna aliqua. Ut enim ad minim veniam, quis nostrud \
                        exercitation ullamco laboris nisi ut aliquip ex ea com\
                        modo consequat. Duis aute irure dolor in reprehenderit\
                         in voluptate velit esse cillum dolore eu fugiat nulla\
                         pariatur. Excepteur sint occaecat cupidatat non proid\
                        ent, sunt in culpa qui officia deserunt mollit anim id\
                         est laborum.".to_string());
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn big_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let r = Rope::from(s);
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_left_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        r = r + iter::repeat('a').take(100).collect::<String>();
    }
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_right_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        let s2 = iter::repeat('a').take(100).collect::<String>();
        r = Rope::from(s2) + r;
    }
    assert!(r.is_balanced());
}

#[test]
fn merge_rebalance_test() {
    let s = "Lorem ipsum dolor sit amet, consectetur adipiscing eli\
             t, sed do eiusmod tempor incididunt ut labore et dolor\
             e magna aliqua. Ut enim ad minim veniam, quis nostrud \
             exercitation ullamco laboris nisi ut aliquip ex ea com\
             modo consequat. Duis aute irure dolor in reprehenderit\
              in voluptate velit esse cillum dolore eu fugiat nulla\
              pariatur. Excepteur sint occaecat cupidatat non proid\
             ent, sunt in culpa qui officia deserunt mollit anim id\
              est laborum.";

     let t = Rope::from(s.to_owned());
     let u = t + s;
     let u = u.rebalance();
     assert!(u.is_balanced());
}

#[test]
fn with_insert_rope_balance_test() {
    let s: String = iter::repeat('a').take(10).collect();
    let mut r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1 = r_1.with_insert_rope(5, Rope::from(t));
    }
    //  this isn't necessary, with_insert_rope() will automatically rebalance
    //      - eliza, 12/18/2016
    // r_1.rebalance();
    assert!(r_1.is_balanced());
}

#[test]
fn with_insert_rope_test_1() {
    let s: String = iter::repeat('a').take(1_000).collect();
    let r_1 = Rope::from(s);
    let mut r_2 = Rope::new();
    for _ in 0..100 {
        let t: String = iter::repeat('a').take(10).collect();
        r_2.with_insert_rope(0, Rope::from(t));
    }
    assert_eq!(r_1, r_2);
}

#[test]
fn with_insert_rope_test_2() {
    let s: String = iter::repeat('a').take(10).collect();
    let mut r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1.with_insert_rope(5, Rope::from(t));
    }

    let q: String = iter::repeat('a').take(1_000).collect();
    let r_2 = Rope::from(q);
    assert_eq!(r_1, r_2);
}

#[test]
fn mutable_insert_rope_test_1() {
    let mut s_1 = Rope::from(String::from("aaaaa"));
    let mut s_2 = Rope::from(String::from("bbbbb"));
    let s_3 = Rope::from(String::from("ccccc"));
    s_2.insert_rope(0, s_3);
    s_1.insert_rope(0, s_2);
    assert_eq!(&s_1, "cccccbbbbbaaaaa");
}

#[test]
fn mutable_insert_str_test_1() {
    let mut s = Rope::from("aaaaa");
    s.insert_str(0, "bbbbb");
    s.insert_str(10, "ccccc");
    assert_eq!(&s, "bbbbbaaaaaccccc");
}

#[test]
fn mutable_insert_char_test_1() {
    let mut s = Rope::from("aaaaa");
    for _ in 0..5 { s.insert(0, 'b')}
    for _ in 0..5 { s.insert(10, 'c')}
    assert_eq!(&s, "bbbbbaaaaaccccc");
}

#[test]
fn with_insert_char_test_1() {
    let s = Rope::from("aaaaa");
    let s_1 = s.with_insert(5, 'b');
    let s_2 = s_1.with_insert(4, 'b');
    let s_3 = s_2.with_insert(3, 'b');
    let s_4 = s_3.with_insert(2, 'b');
    let s_5 = s_4.with_insert(1, 'b');
    assert_eq!(&s, "aaaaa");
    assert_eq!(&s_1, "aaaaab");
    assert_eq!(&s_2, "aaaabab");
    assert_eq!(&s_3, "aaababab");
    assert_eq!(&s_4, "aabababab");
    assert_eq!(&s_5, "ababababab");

}

#[test]
fn with_insert_str_test_1() {
    let s = Rope::from("aaaaa");
    let s_1 = s.with_insert_str(5, "ccccc");
    let s_2 = s_1.with_insert_str(5, "bbbbb");
    assert_eq!(&s, "aaaaa");
    assert_eq!(&s_1, "aaaaccccc");
    assert_eq!(&s_2, "aaaaabbbbbccccc");
}
<commit_msg>rope: fix issue in with_insert_rope test where rope wasn't reassigned<commit_after>use super::Rope;
use std::iter;

#[test]
fn rebalance_test_1() {
    let r = Rope::from("This is a large string \
                        that will need to be rebalanced.".to_string());
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn rebalance_test_2() {
    let r = Rope::from("Lorem ipsum dolor sit amet, consectetur adipiscing eli\
                        t, sed do eiusmod tempor incididunt ut labore et dolor\
                        e magna aliqua. Ut enim ad minim veniam, quis nostrud \
                        exercitation ullamco laboris nisi ut aliquip ex ea com\
                        modo consequat. Duis aute irure dolor in reprehenderit\
                         in voluptate velit esse cillum dolore eu fugiat nulla\
                         pariatur. Excepteur sint occaecat cupidatat non proid\
                        ent, sunt in culpa qui officia deserunt mollit anim id\
                         est laborum.".to_string());
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn big_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let r = Rope::from(s);
    let r = r.rebalance();
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_left_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        r = r + iter::repeat('a').take(100).collect::<String>();
    }
    assert!(r.is_balanced());
}

#[test]
fn repeated_concat_right_rebalance() {
    let s: String = iter::repeat('a').take(10_000).collect();
    let mut r = Rope::from(s);
    for _ in 1..1000 {
        let s2 = iter::repeat('a').take(100).collect::<String>();
        r = Rope::from(s2) + r;
    }
    assert!(r.is_balanced());
}

#[test]
fn merge_rebalance_test() {
    let s = "Lorem ipsum dolor sit amet, consectetur adipiscing eli\
             t, sed do eiusmod tempor incididunt ut labore et dolor\
             e magna aliqua. Ut enim ad minim veniam, quis nostrud \
             exercitation ullamco laboris nisi ut aliquip ex ea com\
             modo consequat. Duis aute irure dolor in reprehenderit\
              in voluptate velit esse cillum dolore eu fugiat nulla\
              pariatur. Excepteur sint occaecat cupidatat non proid\
             ent, sunt in culpa qui officia deserunt mollit anim id\
              est laborum.";

     let t = Rope::from(s.to_owned());
     let u = t + s;
     let u = u.rebalance();
     assert!(u.is_balanced());
}

#[test]
fn with_insert_rope_balance_test() {
    let s: String = iter::repeat('a').take(10).collect();
    let mut r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1 = r_1.with_insert_rope(5, Rope::from(t));
    }
    //  this isn't necessary, with_insert_rope() will automatically rebalance
    //      - eliza, 12/18/2016
    // r_1.rebalance();
    assert!(r_1.is_balanced());
}

#[test]
fn with_insert_rope_test_1() {
    let s: String = iter::repeat('a').take(1_000).collect();
    let r_1 = Rope::from(s);
    let mut r_2 = Rope::new();
    for _ in 0..100 {
        let t: String = iter::repeat('a').take(10).collect();
        r_2.with_insert_rope(0, Rope::from(t));
    }
    assert_eq!(r_1, r_2);
}

#[test]
fn with_insert_rope_test_2() {
    let s: String = iter::repeat('a').take(10).collect();
    let mut r_1 = Rope::from(s);
    for _ in 0..99 {
        let t: String = iter::repeat('a').take(10).collect();
        r_1 = r_1.with_insert_rope(5, Rope::from(t));
    }

    let q: String = iter::repeat('a').take(1_000).collect();
    let r_2 = Rope::from(q);
    assert_eq!(r_1, r_2);
}

#[test]
fn mutable_insert_rope_test_1() {
    let mut s_1 = Rope::from(String::from("aaaaa"));
    let mut s_2 = Rope::from(String::from("bbbbb"));
    let s_3 = Rope::from(String::from("ccccc"));
    s_2.insert_rope(0, s_3);
    s_1.insert_rope(0, s_2);
    assert_eq!(&s_1, "cccccbbbbbaaaaa");
}

#[test]
fn mutable_insert_str_test_1() {
    let mut s = Rope::from("aaaaa");
    s.insert_str(0, "bbbbb");
    s.insert_str(10, "ccccc");
    assert_eq!(&s, "bbbbbaaaaaccccc");
}

#[test]
fn mutable_insert_char_test_1() {
    let mut s = Rope::from("aaaaa");
    for _ in 0..5 { s.insert(0, 'b')}
    for _ in 0..5 { s.insert(10, 'c')}
    assert_eq!(&s, "bbbbbaaaaaccccc");
}

#[test]
fn with_insert_char_test_1() {
    let s = Rope::from("aaaaa");
    let s_1 = s.with_insert(5, 'b');
    let s_2 = s_1.with_insert(4, 'b');
    let s_3 = s_2.with_insert(3, 'b');
    let s_4 = s_3.with_insert(2, 'b');
    let s_5 = s_4.with_insert(1, 'b');
    assert_eq!(&s, "aaaaa");
    assert_eq!(&s_1, "aaaaab");
    assert_eq!(&s_2, "aaaabab");
    assert_eq!(&s_3, "aaababab");
    assert_eq!(&s_4, "aabababab");
    assert_eq!(&s_5, "ababababab");

}

#[test]
fn with_insert_str_test_1() {
    let s = Rope::from("aaaaa");
    let s_1 = s.with_insert_str(5, "ccccc");
    let s_2 = s_1.with_insert_str(5, "bbbbb");
    assert_eq!(&s, "aaaaa");
    assert_eq!(&s_1, "aaaaccccc");
    assert_eq!(&s_2, "aaaaabbbbbccccc");
}
<|endoftext|>


<commit_before>//! Utility for finding uncommitted changes in Git repositories

#![deny(missing_docs)]

use std::error::Error;
use std::path::{PathBuf, Path};
use std::process::Command;

use clap::{App, SubCommand};
use git2::{Repository, Status, StatusOptions};
use itertools::Itertools;
use reqwest::blocking::multipart::Form;
use reqwest::blocking::multipart::Part;
use reqwest::blocking::Client;
use serde::Serialize;

/// Git repository with a list of modified files
#[derive(Serialize, Debug)]
struct GitRepoWithModifications {
    repo_path: String,
    modified_files: Vec<String>,
}

/// Locate the Git repos on the system
fn locate_git_repos() -> Result<Vec<PathBuf>, Box<dyn Error>> {
    let locate_output =
        Command::new("locate")
            .args(&["-r", "\\.git$"])
            .output()?
            .stdout
        ;
    Ok(String::from_utf8(locate_output)?
        .split("\n")
        .map(|p| PathBuf::from(p))
        .collect())
}

/// Given a list Git repos, return the ones which have modifications, including the modifications
fn find_repo_modifications(repo_locations: &Vec<PathBuf>) -> Vec<GitRepoWithModifications> {
    repo_locations
        .iter()
        .map(|p| Repository::open(p))
        .filter_map(Result::ok)
        .filter_map(|repo| {
            let changed_paths = repo.statuses(Some(StatusOptions::new().include_untracked(false)))
                .expect("getting statuses")
                .iter()
                .filter(|se| se.status() != Status::CURRENT)
                .filter_map(|se| se.path().map(String::from))
                .collect::<Vec<_>>()
                ;
            match changed_paths {
                x if !x.is_empty() =>
                    Some(GitRepoWithModifications {
                        repo_path: String::from(repo.path().to_str().unwrap()),
                        modified_files: x,
                    }),
                _ => None,
            }
        })
        .collect()
}

/// TODO : unused
/// Save the modified repos list along with their modifications
fn save_modified_repos(_repos_with_mods: &Vec<GitRepoWithModifications>) -> Result<(), Box<dyn Error>> {
    let form = Form::new()
        .text("database", "wordnet")
        .part("mappings", Part::file(Path::new("git_modified_repo.sms"))?)
        .text("input_file_type", "JSON")
        // .part("input_file", Part::stream("x"))
        ;
    let client = Client::new();
    let _res = client.post("https://localhost/stardog/admin/virtual/import")
        .basic_auth("admin", Some("admin"))
        .multipart(form)
        .send();
    Ok(())
}

///
fn print_modified_repo_json(repos_with_mods: &Vec<GitRepoWithModifications>) {
    println!("{}", repos_with_mods.into_iter()
        .map(|repo_with_mod| serde_json::to_string(&repo_with_mod).unwrap())
        .join("\n"))
}

/// `main()`
fn main() {
    let app_m = App::new("git_repo_monitor")
        .about("Find Git repositories needing attention")
        .subcommand(SubCommand::with_name("print_json")
            .about("Print the list of repositories as JSON"))
        .subcommand(SubCommand::with_name("save_to_stardog")
            .about("Save the list of repositories to Stardog"))
        .get_matches();

    let repos_with_mods =
        find_repo_modifications(&locate_git_repos().expect("Failed to locate repos"));

    match app_m.subcommand_name() {
        Some("print_json") =>
            print_modified_repo_json(&repos_with_mods),
        Some("save_to_stardog") =>
        // save_modified_repos(&repos_with_mods).expect("Save failed");
            println!("save_to_stardog command not implemented"),
        _ =>
            println!("No command specified"),
    }
}

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

    #[test]
    fn find_modified_files() {
        let x: GitRepoWithModifications;
        let y: Repository;
        // TODO : write me
    }
}
<commit_msg>Format with latest rust-analyzer<commit_after>//! Utility for finding uncommitted changes in Git repositories

#![deny(missing_docs)]

use std::error::Error;
use std::path::{Path, PathBuf};
use std::process::Command;

use clap::{App, SubCommand};
use git2::{Repository, Status, StatusOptions};
use itertools::Itertools;
use reqwest::blocking::multipart::Form;
use reqwest::blocking::multipart::Part;
use reqwest::blocking::Client;
use serde::Serialize;

/// Git repository with a list of modified files
#[derive(Serialize, Debug)]
struct GitRepoWithModifications {
    repo_path: String,
    modified_files: Vec<String>,
}

/// Locate the Git repos on the system
fn locate_git_repos() -> Result<Vec<PathBuf>, Box<dyn Error>> {
    let locate_output = Command::new("locate")
        .args(&["-r", "\\.git$"])
        .output()?
        .stdout;
    Ok(String::from_utf8(locate_output)?
        .split("\n")
        .map(|p| PathBuf::from(p))
        .collect())
}

/// Given a list Git repos, return the ones which have modifications, including the modifications
fn find_repo_modifications(repo_locations: &Vec<PathBuf>) -> Vec<GitRepoWithModifications> {
    repo_locations
        .iter()
        .map(|p| Repository::open(p))
        .filter_map(Result::ok)
        .filter_map(|repo| {
            let changed_paths = repo
                .statuses(Some(StatusOptions::new().include_untracked(false)))
                .expect("getting statuses")
                .iter()
                .filter(|se| se.status() != Status::CURRENT)
                .filter_map(|se| se.path().map(String::from))
                .collect::<Vec<_>>();
            match changed_paths {
                x if !x.is_empty() => Some(GitRepoWithModifications {
                    repo_path: String::from(repo.path().to_str().unwrap()),
                    modified_files: x,
                }),
                _ => None,
            }
        })
        .collect()
}

/// TODO : unused
/// Save the modified repos list along with their modifications
fn save_modified_repos(
    _repos_with_mods: &Vec<GitRepoWithModifications>,
) -> Result<(), Box<dyn Error>> {
    let form = Form::new()
        .text("database", "wordnet")
        .part("mappings", Part::file(Path::new("git_modified_repo.sms"))?)
        .text("input_file_type", "JSON")
        // .part("input_file", Part::stream("x"))
        ;
    let client = Client::new();
    let _res = client
        .post("https://localhost/stardog/admin/virtual/import")
        .basic_auth("admin", Some("admin"))
        .multipart(form)
        .send();
    Ok(())
}

///
fn print_modified_repo_json(repos_with_mods: &Vec<GitRepoWithModifications>) {
    println!(
        "{}",
        repos_with_mods
            .into_iter()
            .map(|repo_with_mod| serde_json::to_string(&repo_with_mod).unwrap())
            .join("\n")
    )
}

/// `main()`
fn main() {
    let app_m = App::new("git_repo_monitor")
        .about("Find Git repositories needing attention")
        .subcommand(
            SubCommand::with_name("print_json").about("Print the list of repositories as JSON"),
        )
        .subcommand(
            SubCommand::with_name("save_to_stardog")
                .about("Save the list of repositories to Stardog"),
        )
        .get_matches();

    let repos_with_mods =
        find_repo_modifications(&locate_git_repos().expect("Failed to locate repos"));

    match app_m.subcommand_name() {
        Some("print_json") => print_modified_repo_json(&repos_with_mods),
        Some("save_to_stardog") =>
        // save_modified_repos(&repos_with_mods).expect("Save failed");
        {
            println!("save_to_stardog command not implemented")
        }
        _ => println!("No command specified"),
    }
}

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

    #[test]
    fn find_modified_files() {
        let x: GitRepoWithModifications;
        let y: Repository;
        // TODO : write me
    }
}
<|endoftext|>


<commit_before>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

fn main() {
    let mut sum = 0;
    for i in Vec::new() {
        sum += i;
    }
}
<commit_msg>Create for-loop-unconstrained-element-type-i32-fallback.rs<commit_after>// Copyright 2017 The Rust Project Developers. See the COPYRIGHT
// file at the top-level directory of this distribution and at
// http://rust-lang.org/COPYRIGHT.
//
// 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.

// Test that the type of `sum` falls back to `i32` here,
// and that the for loop desugaring doesn't inferfere with
// that.

fn main() {
    let mut sum = 0;
    for i in Vec::new() {
        sum += i;
    }
}
<|endoftext|>


<commit_before>use calx::{
    compact_bits_by_2, hex_neighbors, spread_bits_by_2, CellVector, Dir6, GridNode, HexGeom,
};
use euclid::vec2;
use std::num::Wrapping;
use std::ops::{Add, Sub};
use terraform::TerrainQuery;

pub const SECTOR_WIDTH: i32 = 40;
pub const SECTOR_HEIGHT: i32 = 20;

/// Unambiguous location in the game world.
#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Debug, Default, Serialize,
         Deserialize)]
pub struct Location {
    pub x: i16,
    pub y: i16,
    pub z: i16,
}

/// The type for a unique location in the game world.
///
/// IMPORTANT: Be careful where you use the simple "location + vec" algebra. That does not take
/// portals into account, and will usually cause unwanted effects near them in high-level code.
/// Most high-level logic should use `Location::jump` to displace locations. This will correctly
/// traverse portals.
impl Location {
    pub fn origin() -> Location { Location { x: 0, y: 0, z: 0 } }

    pub fn new(x: i16, y: i16, z: i16) -> Location { Location { x, y, z } }

    /// Construct a Location from a Morton code representation.
    ///
    /// Use the representation generated with `to_morton`. The odd bits of the low 32 bits are
    /// compacted to x value, the even bits of the low 32 bits to y and the first 16 of the high 32
    /// bits become z.
    pub fn from_morton(morton_code: u64) -> Location {
        let xy = (morton_code & 0xffff_ffff) as u32;
        let x = compact_bits_by_2(xy) as u16;
        let y = compact_bits_by_2(xy >> 1) as u16;
        let z = (morton_code >> 32) as u16;

        unsafe {
            Location {
                x: ::std::mem::transmute(x),
                y: ::std::mem::transmute(y),
                z: ::std::mem::transmute(z),
            }
        }
    }

    /// Turn the Location to a Morton code value.
    ///
    /// Spatially close locations are often numerically close in Morton codes, these are useful for
    /// quadtree-like structures.
    pub fn to_morton(&self) -> u64 {
        let mut ret = 0;
        let x: u16 = unsafe { ::std::mem::transmute(self.x) };
        let y: u16 = unsafe { ::std::mem::transmute(self.y) };
        let z: u16 = unsafe { ::std::mem::transmute(self.z) };
        ret ^= spread_bits_by_2(x as u32) as u64;
        ret ^= (spread_bits_by_2(y as u32) << 1) as u64;
        ret ^= (z as u64) << 32;
        ret
    }

    /// Vector pointing from this location into the other one if the locations
    /// are on the same Euclidean plane.
    pub fn v2_at(&self, other: Location) -> Option<CellVector> {
        if self.z != other.z {
            return None;
        }
        Some(vec2(other.x as i32, other.y as i32) - vec2(self.x as i32, self.y as i32))
    }

    /// Hex distance from this location to the other one, if applicable.
    pub fn distance_from(&self, other: Location) -> Option<i32> {
        if let Some(v) = self.v2_at(other) {
            Some(v.hex_dist())
        } else {
            None
        }
    }

    /// Distance that defaults to max integer value for separate zones.
    ///
    /// Can be used for situations that want a straightforward metric function like A* search.
    pub fn metric_distance(&self, other: Location) -> i32 {
        self.distance_from(other).unwrap_or(i32::max_value())
    }

    pub fn dir6_towards(&self, other: Location) -> Option<Dir6> {
        if let Some(v) = self.v2_at(other) {
            Some(Dir6::from_v2(v))
        } else {
            None
        }
    }

    /// Offset location and follow any portals in target site.
    pub fn jump<T: TerrainQuery, V: Into<CellVector> + Sized>(
        self,
        ctx: &T,
        offset: V,
    ) -> Location {
        let loc = self + offset.into();
        ctx.portal(loc).unwrap_or(loc)
    }

    /// Return `Sector` this location is in.
    pub fn sector(self) -> Sector {
        let (u, v) = self.to_rect_coords();

        Sector::new(
            (u as f32 / SECTOR_WIDTH as f32).floor() as i16,
            (v as f32 / SECTOR_HEIGHT as f32).floor() as i16,
            self.z,
        )
    }

    /// Map location's x, y to rectangular (offset) coordinates.
    pub fn to_rect_coords(self) -> (i32, i32) {
        let u = self.x as i32 - self.y as i32;
        let v = ((self.x as f32 + self.y as f32) / 2.0).floor() as i32;
        (u, v)
    }

    pub fn from_rect_coords(u: i32, v: i32, z: i16) -> Location {
        // Yeah I don't know either how you're supposed to come up with the right ceil/floor
        // juggling, just tweaked it around until it passed all the unit tests.
        let half_u = u as f32 / 2.0;
        Location::new(
            (half_u.ceil() as i32 + v) as i16,
            (v - half_u.floor() as i32) as i16,
            z,
        )
    }
}

impl<V: Into<CellVector>> Add<V> for Location {
    type Output = Location;
    fn add(self, other: V) -> Location {
        let other = other.into();
        Location {
            x: (self.x as i32 + other.x) as i16,
            y: (self.y as i32 + other.y) as i16,
            z: self.z,
        }
    }
}

impl Add<Portal> for Location {
    type Output = Location;
    fn add(self, other: Portal) -> Location {
        Location {
            x: (Wrapping(self.x) + Wrapping(other.dx)).0,
            y: (Wrapping(self.y) + Wrapping(other.dy)).0,
            z: other.z,
        }
    }
}

impl<V: Into<CellVector>> Sub<V> for Location {
    type Output = Location;
    fn sub(self, other: V) -> Location {
        let other = other.into();
        Location {
            x: (self.x as i32 - other.x) as i16,
            y: (self.y as i32 - other.y) as i16,
            z: self.z,
        }
    }
}

impl GridNode for Location {
    fn neighbors(&self) -> Vec<Location> { hex_neighbors(*self).collect() }
}

#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct Portal {
    pub dx: i16,
    pub dy: i16,
    pub z: i16,
}

impl Portal {
    pub fn new(from: Location, to: Location) -> Portal {
        Portal {
            dx: (Wrapping(to.x) - Wrapping(from.x)).0,
            dy: (Wrapping(to.y) - Wrapping(from.y)).0,
            z: to.z,
        }
    }
}

impl Add<Portal> for Portal {
    type Output = Portal;
    fn add(self, other: Portal) -> Portal {
        Portal {
            dx: (Wrapping(self.dx) + Wrapping(other.dx)).0,
            dy: (Wrapping(self.dy) + Wrapping(other.dy)).0,
            z: other.z,
        }
    }
}

/// Non-scrolling screen.
///
/// A sector represents a rectangular chunk of locations that fit on the visual screen. Sector
/// coordinates form their own sector space that tiles the location space with sectors.
#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct Sector {
    pub x: i16,
    pub y: i16,
    pub z: i16,
}

impl Sector {
    pub fn new(x: i16, y: i16, z: i16) -> Sector { Sector { x, y, z } }

    pub fn origin(self) -> Location { self.rect_coord_loc(0, 0) }

    pub fn rect_coord_loc(self, u: i32, v: i32) -> Location {
        Location::from_rect_coords(
            self.x as i32 * SECTOR_WIDTH + u,
            self.y as i32 * SECTOR_HEIGHT + v,
            self.z,
        )
    }

    /// Center location for this sector.
    ///
    /// Usually you want the camera positioned here.
    pub fn center(self) -> Location {
        // XXX: If the width/height are even (as they currently are), there isn't a centered cell.
        self.rect_coord_loc(SECTOR_WIDTH / 2 - 1, SECTOR_HEIGHT / 2 - 1)
    }

    pub fn iter(self) -> impl Iterator<Item = Location> {
        let n = SECTOR_WIDTH * SECTOR_HEIGHT;
        let pitch = SECTOR_WIDTH;
        (0..n).map(move |i| self.rect_coord_loc(i % pitch, i / pitch))
    }

    pub fn taxicab_distance(self, other: Sector) -> i32 {
        ((self.x as i32) - (other.x as i32)).abs()
            + ((self.y as i32) - (other.y as i32)).abs()
            + ((self.z as i32) - (other.z as i32)).abs()
    }
}

#[cfg(test)]
mod test {
    use super::{Location, Sector};
    use euclid::vec2;

    #[test]
    fn test_wraparound() {
        let l1 = Location::new(0, 0, 0);
        let l2 = l1 + vec2(66000, 66000);
        assert_eq!((464, 464), (l2.x, l2.y));
    }

    #[test]
    fn test_morton() {
        use rand::{self, Rng};
        let mut rng = rand::thread_rng();

        for _ in 0..1000 {
            let x = rng.gen::<u64>() & 0xffff_ffff_ffff;
            assert_eq!(x, Location::from_morton(x).to_morton());
        }
    }

    #[test]
    fn test_location_to_sector() {
        let s = Sector::new(0, 0, 0);
        assert_eq!(s.origin(), Location::new(0, 0, 0));

        // Sector division near origin
        assert_eq!(Location::new(0, 0, 0).sector(), Sector::new(0, 0, 0));
        assert_eq!(Location::new(-1, -1, 0).sector(), Sector::new(0, -1, 0));
        assert_eq!(Location::new(0, 1, 0).sector(), Sector::new(-1, 0, 0));
        assert_eq!(Location::new(-1, 0, 0).sector(), Sector::new(-1, -1, 0));

        for y in -100..100 {
            for x in -100..100 {
                let loc = Location::new(x, y, 0);
                let (u, v) = loc.to_rect_coords();
                assert_eq!(
                    loc,
                    Location::from_rect_coords(u, v, loc.z),
                    "u: {}, v: {}",
                    u,
                    v
                );

                assert!(
                    loc.sector().iter().find(|&x| x == loc).is_some(),
                    format!("{:?} not found in sector {:?}", loc, loc.sector())
                );
            }
        }
    }

    #[test]
    fn test_sector_iter() {
        let s = Sector::new(0, 0, 0);

        for loc in s.iter() {
            assert_eq!(s, loc.sector(), "Location: {:?}", loc);
        }
    }

    #[test]
    fn test_distance() {
        let s = Sector::new(1, 2, 3);
        assert_eq!(s.taxicab_distance(s), 0);
        assert_eq!(s.taxicab_distance(Sector::new(3, 2, 1)), 4);
        assert_eq!(Sector::new(3, 2, 1).taxicab_distance(s), 4);
    }
}
<commit_msg>Move some mapgen stuff into Sector<commit_after>use calx::{
    compact_bits_by_2, hex_neighbors, spread_bits_by_2, CellVector, Dir6, GridNode, HexGeom,
};
use euclid::vec2;
use std::num::Wrapping;
use std::ops::{Add, Sub};
use terraform::TerrainQuery;

pub const SECTOR_WIDTH: i32 = 40;
pub const SECTOR_HEIGHT: i32 = 20;

/// Unambiguous location in the game world.
#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Debug, Default, Serialize,
         Deserialize)]
pub struct Location {
    pub x: i16,
    pub y: i16,
    pub z: i16,
}

/// The type for a unique location in the game world.
///
/// IMPORTANT: Be careful where you use the simple "location + vec" algebra. That does not take
/// portals into account, and will usually cause unwanted effects near them in high-level code.
/// Most high-level logic should use `Location::jump` to displace locations. This will correctly
/// traverse portals.
impl Location {
    pub fn origin() -> Location { Location { x: 0, y: 0, z: 0 } }

    pub fn new(x: i16, y: i16, z: i16) -> Location { Location { x, y, z } }

    /// Construct a Location from a Morton code representation.
    ///
    /// Use the representation generated with `to_morton`. The odd bits of the low 32 bits are
    /// compacted to x value, the even bits of the low 32 bits to y and the first 16 of the high 32
    /// bits become z.
    pub fn from_morton(morton_code: u64) -> Location {
        let xy = (morton_code & 0xffff_ffff) as u32;
        let x = compact_bits_by_2(xy) as u16;
        let y = compact_bits_by_2(xy >> 1) as u16;
        let z = (morton_code >> 32) as u16;

        unsafe {
            Location {
                x: ::std::mem::transmute(x),
                y: ::std::mem::transmute(y),
                z: ::std::mem::transmute(z),
            }
        }
    }

    /// Turn the Location to a Morton code value.
    ///
    /// Spatially close locations are often numerically close in Morton codes, these are useful for
    /// quadtree-like structures.
    pub fn to_morton(&self) -> u64 {
        let mut ret = 0;
        let x: u16 = unsafe { ::std::mem::transmute(self.x) };
        let y: u16 = unsafe { ::std::mem::transmute(self.y) };
        let z: u16 = unsafe { ::std::mem::transmute(self.z) };
        ret ^= spread_bits_by_2(x as u32) as u64;
        ret ^= (spread_bits_by_2(y as u32) << 1) as u64;
        ret ^= (z as u64) << 32;
        ret
    }

    /// Vector pointing from this location into the other one if the locations
    /// are on the same Euclidean plane.
    pub fn v2_at(&self, other: Location) -> Option<CellVector> {
        if self.z != other.z {
            return None;
        }
        Some(vec2(other.x as i32, other.y as i32) - vec2(self.x as i32, self.y as i32))
    }

    /// Hex distance from this location to the other one, if applicable.
    pub fn distance_from(&self, other: Location) -> Option<i32> {
        if let Some(v) = self.v2_at(other) {
            Some(v.hex_dist())
        } else {
            None
        }
    }

    /// Distance that defaults to max integer value for separate zones.
    ///
    /// Can be used for situations that want a straightforward metric function like A* search.
    pub fn metric_distance(&self, other: Location) -> i32 {
        self.distance_from(other).unwrap_or(i32::max_value())
    }

    pub fn dir6_towards(&self, other: Location) -> Option<Dir6> {
        if let Some(v) = self.v2_at(other) {
            Some(Dir6::from_v2(v))
        } else {
            None
        }
    }

    /// Offset location and follow any portals in target site.
    pub fn jump<T: TerrainQuery, V: Into<CellVector> + Sized>(
        self,
        ctx: &T,
        offset: V,
    ) -> Location {
        let loc = self + offset.into();
        ctx.portal(loc).unwrap_or(loc)
    }

    /// Return `Sector` this location is in.
    pub fn sector(self) -> Sector {
        let (u, v) = self.to_rect_coords();

        Sector::new(
            (u as f32 / SECTOR_WIDTH as f32).floor() as i16,
            (v as f32 / SECTOR_HEIGHT as f32).floor() as i16,
            self.z,
        )
    }

    /// Map location's x, y to rectangular (offset) coordinates.
    pub fn to_rect_coords(self) -> (i32, i32) {
        let u = self.x as i32 - self.y as i32;
        let v = ((self.x as f32 + self.y as f32) / 2.0).floor() as i32;
        (u, v)
    }

    pub fn from_rect_coords(u: i32, v: i32, z: i16) -> Location {
        // Yeah I don't know either how you're supposed to come up with the right ceil/floor
        // juggling, just tweaked it around until it passed all the unit tests.
        let half_u = u as f32 / 2.0;
        Location::new(
            (half_u.ceil() as i32 + v) as i16,
            (v - half_u.floor() as i32) as i16,
            z,
        )
    }

    /// True for locations at sector corner that are connected to a diagonally adjacent sector.
    ///
    /// Locations that are next to a diagonal section direct travel between two diagonal sectors.
    /// You may want to take care to block these locations during map generation to prevent
    /// unexpected connections if the sector structure is designed with the assumption that sectors
    /// are only connected along cardinal directions.
    pub fn is_next_to_diagonal_sector(self) -> bool {
        let sector = self.sector();

        hex_neighbors(self)
            .map(|x| x.sector().taxicab_distance(sector))
            .any(|d| d > 1)
    }
}

impl<V: Into<CellVector>> Add<V> for Location {
    type Output = Location;
    fn add(self, other: V) -> Location {
        let other = other.into();
        Location {
            x: (self.x as i32 + other.x) as i16,
            y: (self.y as i32 + other.y) as i16,
            z: self.z,
        }
    }
}

impl Add<Portal> for Location {
    type Output = Location;
    fn add(self, other: Portal) -> Location {
        Location {
            x: (Wrapping(self.x) + Wrapping(other.dx)).0,
            y: (Wrapping(self.y) + Wrapping(other.dy)).0,
            z: other.z,
        }
    }
}

impl<V: Into<CellVector>> Sub<V> for Location {
    type Output = Location;
    fn sub(self, other: V) -> Location {
        let other = other.into();
        Location {
            x: (self.x as i32 - other.x) as i16,
            y: (self.y as i32 - other.y) as i16,
            z: self.z,
        }
    }
}

impl GridNode for Location {
    fn neighbors(&self) -> Vec<Location> { hex_neighbors(*self).collect() }
}

#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct Portal {
    pub dx: i16,
    pub dy: i16,
    pub z: i16,
}

impl Portal {
    pub fn new(from: Location, to: Location) -> Portal {
        Portal {
            dx: (Wrapping(to.x) - Wrapping(from.x)).0,
            dy: (Wrapping(to.y) - Wrapping(from.y)).0,
            z: to.z,
        }
    }
}

impl Add<Portal> for Portal {
    type Output = Portal;
    fn add(self, other: Portal) -> Portal {
        Portal {
            dx: (Wrapping(self.dx) + Wrapping(other.dx)).0,
            dy: (Wrapping(self.dy) + Wrapping(other.dy)).0,
            z: other.z,
        }
    }
}

/// Non-scrolling screen.
///
/// A sector represents a rectangular chunk of locations that fit on the visual screen. Sector
/// coordinates form their own sector space that tiles the location space with sectors.
#[derive(Copy, Eq, PartialEq, Clone, Hash, PartialOrd, Ord, Debug, Serialize, Deserialize)]
pub struct Sector {
    pub x: i16,
    pub y: i16,
    pub z: i16,
}

impl Sector {
    pub fn new(x: i16, y: i16, z: i16) -> Sector { Sector { x, y, z } }

    pub fn origin(self) -> Location { self.rect_coord_loc(0, 0) }

    pub fn rect_coord_loc(self, u: i32, v: i32) -> Location {
        Location::from_rect_coords(
            self.x as i32 * SECTOR_WIDTH + u,
            self.y as i32 * SECTOR_HEIGHT + v,
            self.z,
        )
    }

    /// Center location for this sector.
    ///
    /// Usually you want the camera positioned here.
    pub fn center(self) -> Location {
        // XXX: If the width/height are even (as they currently are), there isn't a centered cell.
        self.rect_coord_loc(SECTOR_WIDTH / 2 - 1, SECTOR_HEIGHT / 2 - 1)
    }

    pub fn iter(self) -> impl Iterator<Item = Location> {
        let n = SECTOR_WIDTH * SECTOR_HEIGHT;
        let pitch = SECTOR_WIDTH;
        (0..n).map(move |i| self.rect_coord_loc(i % pitch, i / pitch))
    }

    /// Iterate offset points for a generic `Sector`.
    pub fn points() -> impl Iterator<Item = CellVector> {
        let sector = Sector::new(0, 0, 0);
        let sector_origin = sector.origin();
        sector
            .iter()
            .map(move |loc| sector_origin.v2_at(loc).unwrap())
    }

    pub fn taxicab_distance(self, other: Sector) -> i32 {
        ((self.x as i32) - (other.x as i32)).abs()
            + ((self.y as i32) - (other.y as i32)).abs()
            + ((self.z as i32) - (other.z as i32)).abs()
    }
}

#[cfg(test)]
mod test {
    use super::{Location, Sector};
    use euclid::vec2;

    #[test]
    fn test_wraparound() {
        let l1 = Location::new(0, 0, 0);
        let l2 = l1 + vec2(66000, 66000);
        assert_eq!((464, 464), (l2.x, l2.y));
    }

    #[test]
    fn test_morton() {
        use rand::{self, Rng};
        let mut rng = rand::thread_rng();

        for _ in 0..1000 {
            let x = rng.gen::<u64>() & 0xffff_ffff_ffff;
            assert_eq!(x, Location::from_morton(x).to_morton());
        }
    }

    #[test]
    fn test_location_to_sector() {
        let s = Sector::new(0, 0, 0);
        assert_eq!(s.origin(), Location::new(0, 0, 0));

        // Sector division near origin
        assert_eq!(Location::new(0, 0, 0).sector(), Sector::new(0, 0, 0));
        assert_eq!(Location::new(-1, -1, 0).sector(), Sector::new(0, -1, 0));
        assert_eq!(Location::new(0, 1, 0).sector(), Sector::new(-1, 0, 0));
        assert_eq!(Location::new(-1, 0, 0).sector(), Sector::new(-1, -1, 0));

        for y in -100..100 {
            for x in -100..100 {
                let loc = Location::new(x, y, 0);
                let (u, v) = loc.to_rect_coords();
                assert_eq!(
                    loc,
                    Location::from_rect_coords(u, v, loc.z),
                    "u: {}, v: {}",
                    u,
                    v
                );

                assert!(
                    loc.sector().iter().find(|&x| x == loc).is_some(),
                    format!("{:?} not found in sector {:?}", loc, loc.sector())
                );
            }
        }
    }

    #[test]
    fn test_sector_iter() {
        let s = Sector::new(0, 0, 0);

        for loc in s.iter() {
            assert_eq!(s, loc.sector(), "Location: {:?}", loc);
        }
    }

    #[test]
    fn test_distance() {
        let s = Sector::new(1, 2, 3);
        assert_eq!(s.taxicab_distance(s), 0);
        assert_eq!(s.taxicab_distance(Sector::new(3, 2, 1)), 4);
        assert_eq!(Sector::new(3, 2, 1).taxicab_distance(s), 4);
    }
}
<|endoftext|>


<commit_before>extern crate regex;
extern crate serde_json;

#[macro_use]
extern crate throw;

use throw::Result;

macro_rules! assert_matches {
    ($expected:expr, $actual:expr) => ({
        let expected = ($expected).replace("    ", "\t");
        let re = regex::Regex::new(&expected).expect("expected hardcoded regex to compile");

        let actual = format!("{}", $actual);

        assert!(re.is_match(&actual),
            format!("expected error to match regex `\n{}\n`, but found `\n{}\n`", expected, actual));
    })
}

fn throw_static_message() -> Result<(), &'static str> {
    throw_new!("hi");
}

fn throw1() -> Result<(), ()> {
    throw_new!(());
}

fn throw2() -> Result<(), ()> {
    up!(throw1());
    Ok(())
}

fn throw3() -> Result<(), ()> {
    up!(throw2());
    Ok(())
}

fn throw_with_context1() -> Result<(), &'static str> {
    throw_new!("Error with context", "code"=>78,"application"=>"rust_core")
}

fn throw_with_context2() -> Result<(), &'static str> {
    up!(throw_with_context1(), "project_secret"=>"omega");
    Ok(())
}

fn throw_with_context3() -> Result<(), &'static str> {
    up!(throw_with_context2(),  "score"=>0.75, "height"=>948);
    Ok(())
}

fn gives_ok() -> Result<&'static str, &'static str> {
    Ok("ok")
}

fn throws_ok() -> Result<&'static str, &'static str> {
    let ok_msg = up!(gives_ok());
    Ok(ok_msg)
}

mod mod_test {
    use throw::Result;

    pub fn throws() -> Result<(), &'static str> {
        throw_new!("ahhhh");
    }
}

fn throws_into() -> Result<(), String> {
    throw!(Err("some static string"));
    Ok(())
}

fn throws_into_key_value() -> Result<(), String> {
    throw!(Err("some static string"), "key" => "value");
    Ok(())
}

#[test]
fn test_static_message() {
    let error = throw_static_message().unwrap_err();
    assert_eq!(*error.error(), "hi");
    assert_matches!(
        r#"Error: hi
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        error
    );
    assert_eq!("hi".to_owned(), error.into_origin());
}

#[test]
#[cfg(any(feature = "serde-1", feature = "serde-1-std"))]
fn serialize_json() {
    let error = throw_with_context3().unwrap_err();
    let json = serde_json::to_string(&error).unwrap();
    assert_eq!(r#"{"points":[{"line":40,"column":5,"module_path":"exceptions_work","file":"tests/exceptions_work.rs"},{"line":44,"column":5,"module_path":"exceptions_work","file":"tests/exceptions_work.rs"},{"line":49,"column":5,"module_path":"exceptions_work","file":"tests/exceptions_work.rs"}],"context":[{"key":"code","value":78},{"key":"application","value":"rust_core"},{"key":"project_secret","value":"omega"},{"key":"score","value":0.75},{"key":"height","value":948}],"error":"Error with context"}"#, json);
}

#[test]
fn test_throw_with_context() {
    let error = throw_with_context1().unwrap_err();
    assert_eq!(error.get_context().len(), 2);

    let error2 = throw_with_context3().unwrap_err();
    assert_eq!(error2.get_context().len(), 5);

    assert_matches!(
        r#"Error: "Error with context"
    height: 948
	score: 0.75
	project_secret: omega
	application: rust_core
	code: 78
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        format!("{:?}", error2)
    );
}

#[test]
#[allow(deprecated)]
fn test_multiple_throws() {
    let error = throw3().unwrap_err();
    assert_eq!(error.error(), &());
    assert_eq!(error.error(), error.original_error());
    assert_matches!(
        r#"Error: \(\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        format!("{:?}", error)
    );
}

#[test]
fn test_returns_ok() {
    let ok = throws_ok().unwrap();
    assert_eq!(ok, "ok");
}

#[test]
fn test_mod_throw() {
    let error = mod_test::throws().unwrap_err();
    assert_matches!(
        r#"Error: ahhhh
    at [0-9]+:[0-9] in exceptions_work::mod_test \([a-z/._-]+\)"#,
        error
    );
}

#[test]
fn test_throws_into() {
    let error = throws_into().unwrap_err();
    assert_matches!(
        r#"Error: some static string
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        error
    )
}

#[test]
fn test_throws_into_key_value() {
    let error = throws_into_key_value().unwrap_err();
    assert_matches!(
        r#"Error: some static string
    key: value
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        error
    )
}
<commit_msg>Add test case covering trailing commas and multiple key/value pairs in throw!().<commit_after>extern crate regex;
extern crate serde_json;

#[macro_use]
extern crate throw;

use throw::Result;

macro_rules! assert_matches {
    ($expected:expr, $actual:expr) => ({
        let expected = ($expected).replace("    ", "\t");
        let re = regex::Regex::new(&expected).expect("expected hardcoded regex to compile");

        let actual = format!("{}", $actual);

        assert!(re.is_match(&actual),
            format!("expected error to match regex `\n{}\n`, but found `\n{}\n`", expected, actual));
    })
}

fn throw_static_message() -> Result<(), &'static str> {
    throw_new!("hi");
}

fn throw1() -> Result<(), ()> {
    throw_new!(());
}

fn throw2() -> Result<(), ()> {
    up!(throw1());
    Ok(())
}

fn throw3() -> Result<(), ()> {
    up!(throw2());
    Ok(())
}

fn throw_with_context1() -> Result<(), &'static str> {
    throw_new!("Error with context", "code"=>78,"application"=>"rust_core")
}

fn throw_with_context2() -> Result<(), &'static str> {
    up!(throw_with_context1(), "project_secret"=>"omega");
    Ok(())
}

fn throw_with_context3() -> Result<(), &'static str> {
    up!(throw_with_context2(),  "score"=>0.75, "height"=>948);
    Ok(())
}

fn gives_ok() -> Result<&'static str, &'static str> {
    Ok("ok")
}

fn throws_ok() -> Result<&'static str, &'static str> {
    let ok_msg = up!(gives_ok());
    Ok(ok_msg)
}

mod mod_test {
    use throw::Result;

    pub fn throws() -> Result<(), &'static str> {
        throw_new!("ahhhh");
    }
}

fn throws_into() -> Result<(), String> {
    throw!(Err("some static string"));
    Ok(())
}

fn throws_into_key_value() -> Result<(), String> {
    throw!(Err("some static string"), "key" => "value");
    Ok(())
}

fn throws_into_multiple_key_value_pairs() -> Result<(), String> {
    throw!(
        Err("some static string"),
        "key" => "value",
        "key2" => "value2",
        "key3" => "value3",
        "key4" => "value4",
    );
    Ok(())
}

#[test]
fn test_static_message() {
    let error = throw_static_message().unwrap_err();
    assert_eq!(*error.error(), "hi");
    assert_matches!(
        r#"Error: hi
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        error
    );
    assert_eq!("hi".to_owned(), error.into_origin());
}

#[test]
#[cfg(any(feature = "serde-1", feature = "serde-1-std"))]
fn serialize_json() {
    let error = throw_with_context3().unwrap_err();
    let json = serde_json::to_string(&error).unwrap();
    assert_eq!(r#"{"points":[{"line":40,"column":5,"module_path":"exceptions_work","file":"tests/exceptions_work.rs"},{"line":44,"column":5,"module_path":"exceptions_work","file":"tests/exceptions_work.rs"},{"line":49,"column":5,"module_path":"exceptions_work","file":"tests/exceptions_work.rs"}],"context":[{"key":"code","value":78},{"key":"application","value":"rust_core"},{"key":"project_secret","value":"omega"},{"key":"score","value":0.75},{"key":"height","value":948}],"error":"Error with context"}"#, json);
}

#[test]
fn test_throw_with_context() {
    let error = throw_with_context1().unwrap_err();
    assert_eq!(error.get_context().len(), 2);

    let error2 = throw_with_context3().unwrap_err();
    assert_eq!(error2.get_context().len(), 5);

    assert_matches!(
        r#"Error: "Error with context"
    height: 948
	score: 0.75
	project_secret: omega
	application: rust_core
	code: 78
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        format!("{:?}", error2)
    );
}

#[test]
#[allow(deprecated)]
fn test_multiple_throws() {
    let error = throw3().unwrap_err();
    assert_eq!(error.error(), &());
    assert_eq!(error.error(), error.original_error());
    assert_matches!(
        r#"Error: \(\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        format!("{:?}", error)
    );
}

#[test]
fn test_returns_ok() {
    let ok = throws_ok().unwrap();
    assert_eq!(ok, "ok");
}

#[test]
fn test_mod_throw() {
    let error = mod_test::throws().unwrap_err();
    assert_matches!(
        r#"Error: ahhhh
    at [0-9]+:[0-9] in exceptions_work::mod_test \([a-z/._-]+\)"#,
        error
    );
}

#[test]
fn test_throws_into() {
    let error = throws_into().unwrap_err();
    assert_matches!(
        r#"Error: some static string
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        error
    )
}

#[test]
fn test_throws_into_key_value() {
    let error = throws_into_key_value().unwrap_err();
    assert_matches!(
        r#"Error: some static string
    key: value
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        error
    )
}

#[test]
fn test_throws_into_multiple_key_value_pairs() {
    let error = throws_into_multiple_key_value_pairs().unwrap_err();
    assert_matches!(
        r#"Error: some static string
    key4: value4
    key3: value3
    key2: value2
    key: value
    at [0-9]+:[0-9] in exceptions_work \([a-z/._-]+\)"#,
        error
    )
}
<|endoftext|>


<commit_before>use schema::Schema;
use schema::Document;
use indexer::SegmentSerializer;
use core::Index;
use core::SerializableSegment;
use core::Segment;
use std::thread::JoinHandle;
use indexer::SegmentWriter;
use std::clone::Clone;
use std::io;
use std::path::Path;
use std::thread;
use std::collections::HashSet;
use indexer::merger::IndexMerger;
use core::SegmentId;
use datastruct::stacker::Heap;
use std::mem::swap;
use chan;
use directory::WritePtr;

use Result;
use Error;

// Size of the margin for the heap. A segment is closed when the remaining memory
// in the heap goes below MARGIN_IN_BYTES.
pub const MARGIN_IN_BYTES: u32 = 10_000_000u32;

// We impose the memory per thread to be at least 30 MB.
pub const HEAP_SIZE_LIMIT: u32 = MARGIN_IN_BYTES * 3u32;

// Add document will block if the number of docs waiting in the queue to be indexed reaches PIPELINE_MAX_SIZE_IN_DOCS
const PIPELINE_MAX_SIZE_IN_DOCS: usize = 10_000;

pub const LOCKFILE_NAME: &'static str = ".tantivy-indexer.lock";

type DocumentSender = chan::Sender<Document>;
type DocumentReceiver = chan::Receiver<Document>;

type NewSegmentSender = chan::Sender<Result<(SegmentId, usize)>>;
type NewSegmentReceiver = chan::Receiver<Result<(SegmentId, usize)>>;

/// `IndexWriter` is the user entry-point to add document to an index.
///
/// It manages a small number of indexing thread, as well as a shared
/// indexing queue.
/// Each indexing thread builds its own independant `Segment`, via
/// a `SegmentWriter` object.
pub struct IndexWriter {
	index: Index,
	heap_size_in_bytes_per_thread: usize,
	workers_join_handle: Vec<JoinHandle<()>>,
	segment_ready_sender: NewSegmentSender,
	segment_ready_receiver: NewSegmentReceiver,
	document_receiver: DocumentReceiver,
	document_sender: DocumentSender,
	num_threads: usize,
	  docstamp: u64,
}

fn index_documents(heap: &mut Heap,
				   segment: Segment,
				   schema: &Schema,
				   document_iterator: &mut Iterator<Item=Document>) -> Result<usize> {
	heap.clear();
	let mut segment_writer = try!(SegmentWriter::for_segment(heap, segment, &schema));
	for doc in document_iterator {
		try!(segment_writer.add_document(&doc, &schema));
		if segment_writer.is_buffer_full() {
			info!("Buffer limit reached, flushing segment with maxdoc={}.", segment_writer.max_doc());
			break;
		}
	}
	let num_docs = segment_writer.max_doc() as usize;
	try!(segment_writer.finalize());
	Ok(num_docs)
}

impl Drop for IndexWriter {
    fn drop(&mut self) {
        let lockfile_path = Path::new(LOCKFILE_NAME);
        match self.index.directory_mut().delete(lockfile_path) {
            Ok(_) => (),
            Err(_) => ()
        }
    }
}
impl IndexWriter {

	/// Spawns a new worker thread for indexing.
	/// The thread consumes documents from the pipeline.
	///
	fn add_indexing_worker(&mut self,) -> Result<()> {
		let index = self.index.clone();
		let schema = self.index.schema();
		let segment_ready_sender_clone = self.segment_ready_sender.clone();
		let document_receiver_clone = self.document_receiver.clone();

		let mut heap = Heap::with_capacity(self.heap_size_in_bytes_per_thread);
		let join_handle: JoinHandle<()> = thread::spawn(move || {
			loop {
				let segment = index.new_segment();
				let segment_id = segment.id();
				let mut document_iterator = document_receiver_clone
					.clone()
					.into_iter()
					.peekable();
				// the peeking here is to avoid
				// creating a new segment's files
				// if no document are available.
				if document_iterator.peek().is_some() {
					let index_result = index_documents(&mut heap, segment, &schema, &mut document_iterator)
						.map(|num_docs| (segment_id, num_docs));
					segment_ready_sender_clone.send(index_result);
				}
				else {
					return;
				}
			}
		});
		self.workers_join_handle.push(join_handle);
		Ok(())
	}

	/// Open a new index writer
	///
	/// num_threads tells the number of indexing worker that
	/// should work at the same time.
	pub fn open(index: &Index,
				num_threads: usize,
				heap_size_in_bytes_per_thread: usize) -> Result<IndexWriter> {
		if heap_size_in_bytes_per_thread <= HEAP_SIZE_LIMIT as usize {
			panic!(format!("The heap size per thread needs to be at least {}.", HEAP_SIZE_LIMIT));
		}

      let mut cloned_index = index.clone();
      let lockfile_path = Path::new(LOCKFILE_NAME);

      try!(cloned_index.directory_mut().open_write(lockfile_path));
		let (document_sender, document_receiver): (DocumentSender, DocumentReceiver) = chan::sync(PIPELINE_MAX_SIZE_IN_DOCS);
		let (segment_ready_sender, segment_ready_receiver): (NewSegmentSender, NewSegmentReceiver) = chan::async();
		let mut index_writer = IndexWriter {
			heap_size_in_bytes_per_thread: heap_size_in_bytes_per_thread,
			index: cloned_index,
			segment_ready_receiver: segment_ready_receiver,
			segment_ready_sender: segment_ready_sender,
			document_receiver: document_receiver,
			document_sender: document_sender,
			workers_join_handle: Vec::new(),
			num_threads: num_threads,
			docstamp: try!(index.docstamp()),
		};
		try!(index_writer.start_workers());
		Ok(index_writer)
	}

	fn start_workers(&mut self,) -> Result<()> {
		for _ in 0 .. self.num_threads {
			try!(self.add_indexing_worker());
		}
		Ok(())
	}

	/// Merges a given list of segments
	pub fn merge(&mut self, segments: &[Segment]) -> Result<()> {
		let schema = self.index.schema();
		let merger = try!(IndexMerger::open(schema, segments));
		let mut merged_segment = self.index.new_segment();
		let segment_serializer = try!(SegmentSerializer::for_segment(&mut merged_segment));
		try!(merger.write(segment_serializer));
		let merged_segment_ids: HashSet<SegmentId> = segments.iter().map(|segment| segment.id()).collect();
		try!(self.index.publish_merge_segment(merged_segment_ids, merged_segment.id()));
		Ok(())
	}

	/// Closes the current document channel send.
	/// and replace all the channels by new ones.
	///
	/// The current workers will keep on indexing
	/// the pending document and stop
	/// when no documents are remaining.
	///
	/// Returns the former segment_ready channel.
	fn recreate_channels(&mut self,) -> (DocumentReceiver, chan::Receiver<Result<(SegmentId, usize)>>) {
		let (mut document_sender, mut document_receiver): (DocumentSender, DocumentReceiver) = chan::sync(PIPELINE_MAX_SIZE_IN_DOCS);
		let (mut segment_ready_sender, mut segment_ready_receiver): (NewSegmentSender, NewSegmentReceiver) = chan::async();
		swap(&mut self.document_sender, &mut document_sender);
		swap(&mut self.document_receiver, &mut document_receiver);
		swap(&mut self.segment_ready_sender, &mut segment_ready_sender);
		swap(&mut self.segment_ready_receiver, &mut segment_ready_receiver);
		(document_receiver, segment_ready_receiver)
	}

	/// Rollback to the last commit
	///
	/// This cancels all of the update that
	/// happened before after the last commit.
	/// After calling rollback, the index is in the same
	/// state as it was after the last commit.
	///
	/// The docstamp at the last commit is returned.
	pub fn rollback(&mut self,) -> Result<u64> {

		// we cannot drop segment ready receiver yet
		// as it would block the workers.
		let (document_receiver, mut _segment_ready_receiver) = self.recreate_channels();

		// consumes the document receiver pipeline
		// worker don't need to index the pending documents.
		for _ in document_receiver {};

		let mut former_workers_join_handle = Vec::new();
		swap(&mut former_workers_join_handle, &mut self.workers_join_handle);

		// wait for all the worker to finish their work
		// (it should be fast since we consumed all pending documents)
		for worker_handle in former_workers_join_handle {
			try!(worker_handle
				.join()
				.map_err(|e| Error::ErrorInThread(format!("{:?}", e)))
			);
			// add a new worker for the next generation.
			try!(self.add_indexing_worker());
		}

		// reset the docstamp to what it was before
		self.docstamp = try!(self.index.docstamp());
		Ok(self.docstamp)
	}

	/// Commits all of the pending changes
	///
	/// A call to commit blocks.
	/// After it returns, all of the document that
	/// were added since the last commit are published
	/// and persisted.
	///
	/// In case of a crash or an hardware failure (as
	/// long as the hard disk is spared), it will be possible
	/// to resume indexing from this point.
	///
	/// Commit returns the `docstamp` of the last document
	/// that made it in the commit.
	///
	pub fn commit(&mut self,) -> Result<u64> {

		let (document_receiver, segment_ready_receiver) = self.recreate_channels();
		drop(document_receiver);

		// Docstamp of the last document in this commit.
		let commit_docstamp = self.docstamp;

		let mut former_workers_join_handle = Vec::new();
		swap(&mut former_workers_join_handle, &mut self.workers_join_handle);

		for worker_handle in former_workers_join_handle {
			try!(worker_handle
				.join()
				.map_err(|e| Error::ErrorInThread(format!("{:?}", e)))
			);
			// add a new worker for the next generation.
			try!(self.add_indexing_worker());
		}

		let segment_ids_and_size: Vec<(SegmentId, usize)> = try!(
			segment_ready_receiver
				.into_iter()
				.collect()
		);

		let segment_ids: Vec<SegmentId> = segment_ids_and_size
			.iter()
			.map(|&(segment_id, _num_docs)| segment_id)
			.collect();

		try!(self.index.publish_segments(&segment_ids, commit_docstamp));

		Ok(commit_docstamp)
	}

	/// Adds a document.
	///
	/// If the indexing pipeline is full, this call may block.
	///
	/// The docstamp is an increasing `u64` that can
	/// be used by the client to align commits with its own
	/// document queue.
	///
	/// Currently it represents the number of documents that
	/// have been added since the creation of the index.
	pub fn add_document(&mut self, doc: Document) -> io::Result<u64> {
		self.document_sender.send(doc);
		self.docstamp += 1;
		Ok(self.docstamp)
	}

}

#[cfg(test)]
mod tests {

	use schema::{self, Document};
	use Index;
	use Term;
    use Error;
    use directory::error::OpenWriteError;

    #[test]
    fn test_lockfile_stops_duplicates() {

		    let mut schema_builder = schema::SchemaBuilder::default();
		    let text_field = schema_builder.add_text_field("text", schema::TEXT);
		    let index = Index::create_in_ram(schema_builder.build());
			  let index_writer = index.writer(40_000_000).unwrap();
        match index.writer(40_000_000) {
            Err(Error::FileAlreadyExists(_)) => {},
            _ => panic!("Expected FileAlreadyExists error")
        }
    }

    #[test]
    fn test_lockfile_released_on_drop() {
		    let mut schema_builder = schema::SchemaBuilder::default();
		    let text_field = schema_builder.add_text_field("text", schema::TEXT);
		    let index = Index::create_in_ram(schema_builder.build());
			  {
            let index_writer = index.writer(40_000_000).unwrap();
        }

        let index_writer_two = index.writer(40_000_000).unwrap();
    }
	#[test]
	fn test_commit_and_rollback() {
		let mut schema_builder = schema::SchemaBuilder::default();
		let text_field = schema_builder.add_text_field("text", schema::TEXT);
		let index = Index::create_in_ram(schema_builder.build());

		let num_docs_containing = |s: &str| {
			let searcher = index.searcher();
			let term_a = Term::from_field_text(text_field, s);
			searcher.doc_freq(&term_a)
		};

		{
			// writing the segment
			let mut index_writer = index.writer_with_num_threads(3, 40_000_000).unwrap();
			{
				let mut doc = Document::default();
				doc.add_text(text_field, "a");
				index_writer.add_document(doc).unwrap();
			}
			assert_eq!(index_writer.rollback().unwrap(), 0u64);
			assert_eq!(num_docs_containing("a"), 0);

			{
				let mut doc = Document::default();
				doc.add_text(text_field, "b");
				index_writer.add_document(doc).unwrap();
			}
			{
				let mut doc = Document::default();
				doc.add_text(text_field, "c");
				index_writer.add_document(doc).unwrap();
			}
			assert_eq!(index_writer.commit().unwrap(), 2u64);

			assert_eq!(num_docs_containing("a"), 0);
			assert_eq!(num_docs_containing("b"), 1);
			assert_eq!(num_docs_containing("c"), 1);
		}
		index.searcher();
	}

}
<commit_msg>formatting and removing some unused vars and imports<commit_after>use schema::Schema;
use schema::Document;
use indexer::SegmentSerializer;
use core::Index;
use core::SerializableSegment;
use core::Segment;
use std::thread::JoinHandle;
use indexer::SegmentWriter;
use std::clone::Clone;
use std::io;
use std::path::Path;
use std::thread;
use std::collections::HashSet;
use indexer::merger::IndexMerger;
use core::SegmentId;
use datastruct::stacker::Heap;
use std::mem::swap;
use chan;

use Result;
use Error;

// Size of the margin for the heap. A segment is closed when the remaining memory
// in the heap goes below MARGIN_IN_BYTES.
pub const MARGIN_IN_BYTES: u32 = 10_000_000u32;

// We impose the memory per thread to be at least 30 MB.
pub const HEAP_SIZE_LIMIT: u32 = MARGIN_IN_BYTES * 3u32;

// Add document will block if the number of docs waiting in the queue to be indexed reaches PIPELINE_MAX_SIZE_IN_DOCS
const PIPELINE_MAX_SIZE_IN_DOCS: usize = 10_000;

pub const LOCKFILE_NAME: &'static str = ".tantivy-indexer.lock";

type DocumentSender = chan::Sender<Document>;
type DocumentReceiver = chan::Receiver<Document>;

type NewSegmentSender = chan::Sender<Result<(SegmentId, usize)>>;
type NewSegmentReceiver = chan::Receiver<Result<(SegmentId, usize)>>;

/// `IndexWriter` is the user entry-point to add document to an index.
///
/// It manages a small number of indexing thread, as well as a shared
/// indexing queue.
/// Each indexing thread builds its own independant `Segment`, via
/// a `SegmentWriter` object.
pub struct IndexWriter {
    index: Index,
    heap_size_in_bytes_per_thread: usize,
    workers_join_handle: Vec<JoinHandle<()>>,
    segment_ready_sender: NewSegmentSender,
    segment_ready_receiver: NewSegmentReceiver,
    document_receiver: DocumentReceiver,
    document_sender: DocumentSender,
    num_threads: usize,
    docstamp: u64,
}

fn index_documents(heap: &mut Heap,
                   segment: Segment,
                   schema: &Schema,
                   document_iterator: &mut Iterator<Item = Document>)
                   -> Result<usize> {
    heap.clear();
    let mut segment_writer = try!(SegmentWriter::for_segment(heap, segment, &schema));
    for doc in document_iterator {
        try!(segment_writer.add_document(&doc, &schema));
        if segment_writer.is_buffer_full() {
            info!("Buffer limit reached, flushing segment with maxdoc={}.",
                  segment_writer.max_doc());
            break;
        }
    }
    let num_docs = segment_writer.max_doc() as usize;
    try!(segment_writer.finalize());
    Ok(num_docs)
}

impl Drop for IndexWriter {
    fn drop(&mut self) {
        let lockfile_path = Path::new(LOCKFILE_NAME);
        match self.index.directory_mut().delete(lockfile_path) {
            Ok(_) => (),
            Err(_) => (),
        }
    }
}
impl IndexWriter {
    /// Spawns a new worker thread for indexing.
    /// The thread consumes documents from the pipeline.
    ///
    fn add_indexing_worker(&mut self) -> Result<()> {
        let index = self.index.clone();
        let schema = self.index.schema();
        let segment_ready_sender_clone = self.segment_ready_sender.clone();
        let document_receiver_clone = self.document_receiver.clone();

        let mut heap = Heap::with_capacity(self.heap_size_in_bytes_per_thread);
        let join_handle: JoinHandle<()> = thread::spawn(move || {
            loop {
                let segment = index.new_segment();
                let segment_id = segment.id();
                let mut document_iterator = document_receiver_clone.clone()
                    .into_iter()
                    .peekable();
                // the peeking here is to avoid
                // creating a new segment's files
                // if no document are available.
                if document_iterator.peek().is_some() {
                    let index_result =
                        index_documents(&mut heap, segment, &schema, &mut document_iterator)
                            .map(|num_docs| (segment_id, num_docs));
                    segment_ready_sender_clone.send(index_result);
                } else {
                    return;
                }
            }
        });
        self.workers_join_handle.push(join_handle);
        Ok(())
    }

    /// Open a new index writer
    ///
    /// num_threads tells the number of indexing worker that
    /// should work at the same time.
    pub fn open(index: &Index,
                num_threads: usize,
                heap_size_in_bytes_per_thread: usize)
                -> Result<IndexWriter> {
        if heap_size_in_bytes_per_thread <= HEAP_SIZE_LIMIT as usize {
            panic!(format!("The heap size per thread needs to be at least {}.",
                           HEAP_SIZE_LIMIT));
        }

        let mut cloned_index = index.clone();
        let lockfile_path = Path::new(LOCKFILE_NAME);

        try!(cloned_index.directory_mut().open_write(lockfile_path));
        let (document_sender, document_receiver): (DocumentSender, DocumentReceiver) =
            chan::sync(PIPELINE_MAX_SIZE_IN_DOCS);
        let (segment_ready_sender, segment_ready_receiver): (NewSegmentSender, NewSegmentReceiver) = chan::async();
        let mut index_writer = IndexWriter {
            heap_size_in_bytes_per_thread: heap_size_in_bytes_per_thread,
            index: cloned_index,
            segment_ready_receiver: segment_ready_receiver,
            segment_ready_sender: segment_ready_sender,
            document_receiver: document_receiver,
            document_sender: document_sender,
            workers_join_handle: Vec::new(),
            num_threads: num_threads,
            docstamp: try!(index.docstamp()),
        };
        try!(index_writer.start_workers());
        Ok(index_writer)
    }

    fn start_workers(&mut self) -> Result<()> {
        for _ in 0..self.num_threads {
            try!(self.add_indexing_worker());
        }
        Ok(())
    }

    /// Merges a given list of segments
    pub fn merge(&mut self, segments: &[Segment]) -> Result<()> {
        let schema = self.index.schema();
        let merger = try!(IndexMerger::open(schema, segments));
        let mut merged_segment = self.index.new_segment();
        let segment_serializer = try!(SegmentSerializer::for_segment(&mut merged_segment));
        try!(merger.write(segment_serializer));
        let merged_segment_ids: HashSet<SegmentId> =
            segments.iter().map(|segment| segment.id()).collect();
        try!(self.index.publish_merge_segment(merged_segment_ids, merged_segment.id()));
        Ok(())
    }

    /// Closes the current document channel send.
    /// and replace all the channels by new ones.
    ///
    /// The current workers will keep on indexing
    /// the pending document and stop
    /// when no documents are remaining.
    ///
    /// Returns the former segment_ready channel.
    fn recreate_channels(&mut self)
                         -> (DocumentReceiver, chan::Receiver<Result<(SegmentId, usize)>>) {
        let (mut document_sender, mut document_receiver): (DocumentSender, DocumentReceiver) =
            chan::sync(PIPELINE_MAX_SIZE_IN_DOCS);
        let (mut segment_ready_sender, mut segment_ready_receiver): (NewSegmentSender,
                                                                     NewSegmentReceiver) =
            chan::async();
        swap(&mut self.document_sender, &mut document_sender);
        swap(&mut self.document_receiver, &mut document_receiver);
        swap(&mut self.segment_ready_sender, &mut segment_ready_sender);
        swap(&mut self.segment_ready_receiver,
             &mut segment_ready_receiver);
        (document_receiver, segment_ready_receiver)
    }

    /// Rollback to the last commit
    ///
    /// This cancels all of the update that
    /// happened before after the last commit.
    /// After calling rollback, the index is in the same
    /// state as it was after the last commit.
    ///
    /// The docstamp at the last commit is returned.
    pub fn rollback(&mut self) -> Result<u64> {

        // we cannot drop segment ready receiver yet
        // as it would block the workers.
        let (document_receiver, mut _segment_ready_receiver) = self.recreate_channels();

        // consumes the document receiver pipeline
        // worker don't need to index the pending documents.
        for _ in document_receiver {}

        let mut former_workers_join_handle = Vec::new();
        swap(&mut former_workers_join_handle,
             &mut self.workers_join_handle);

        // wait for all the worker to finish their work
        // (it should be fast since we consumed all pending documents)
        for worker_handle in former_workers_join_handle {
            try!(worker_handle.join()
                .map_err(|e| Error::ErrorInThread(format!("{:?}", e))));
            // add a new worker for the next generation.
            try!(self.add_indexing_worker());
        }

        // reset the docstamp to what it was before
        self.docstamp = try!(self.index.docstamp());
        Ok(self.docstamp)
    }

    /// Commits all of the pending changes
    ///
    /// A call to commit blocks.
    /// After it returns, all of the document that
    /// were added since the last commit are published
    /// and persisted.
    ///
    /// In case of a crash or an hardware failure (as
    /// long as the hard disk is spared), it will be possible
    /// to resume indexing from this point.
    ///
    /// Commit returns the `docstamp` of the last document
    /// that made it in the commit.
    ///
    pub fn commit(&mut self) -> Result<u64> {

        let (document_receiver, segment_ready_receiver) = self.recreate_channels();
        drop(document_receiver);

        // Docstamp of the last document in this commit.
        let commit_docstamp = self.docstamp;

        let mut former_workers_join_handle = Vec::new();
        swap(&mut former_workers_join_handle,
             &mut self.workers_join_handle);

        for worker_handle in former_workers_join_handle {
            try!(worker_handle.join()
                .map_err(|e| Error::ErrorInThread(format!("{:?}", e))));
            // add a new worker for the next generation.
            try!(self.add_indexing_worker());
        }

        let segment_ids_and_size: Vec<(SegmentId, usize)> = try!(segment_ready_receiver.into_iter()
            .collect());

        let segment_ids: Vec<SegmentId> = segment_ids_and_size.iter()
            .map(|&(segment_id, _num_docs)| segment_id)
            .collect();

        try!(self.index.publish_segments(&segment_ids, commit_docstamp));

        Ok(commit_docstamp)
    }

    /// Adds a document.
    ///
    /// If the indexing pipeline is full, this call may block.
    ///
    /// The docstamp is an increasing `u64` that can
    /// be used by the client to align commits with its own
    /// document queue.
    ///
    /// Currently it represents the number of documents that
    /// have been added since the creation of the index.
    pub fn add_document(&mut self, doc: Document) -> io::Result<u64> {
        self.document_sender.send(doc);
        self.docstamp += 1;
        Ok(self.docstamp)
    }
}

#[cfg(test)]
mod tests {

    use schema::{self, Document};
    use Index;
    use Term;
    use Error;

    #[test]
    fn test_lockfile_stops_duplicates() {

        let schema_builder = schema::SchemaBuilder::default();
        let index = Index::create_in_ram(schema_builder.build());
        let index_writer = index.writer(40_000_000).unwrap();
        match index.writer(40_000_000) {
            Err(Error::FileAlreadyExists(_)) => {}
            _ => panic!("Expected FileAlreadyExists error"),
        }
    }

    #[test]
    fn test_lockfile_released_on_drop() {
        let schema_builder = schema::SchemaBuilder::default();
        let index = Index::create_in_ram(schema_builder.build());
        {
            let index_writer = index.writer(40_000_000).unwrap();
        }

        let index_writer_two = index.writer(40_000_000).unwrap();
    }
    #[test]
    fn test_commit_and_rollback() {
        let mut schema_builder = schema::SchemaBuilder::default();
        let text_field = schema_builder.add_text_field("text", schema::TEXT);
        let index = Index::create_in_ram(schema_builder.build());

        let num_docs_containing = |s: &str| {
            let searcher = index.searcher();
            let term_a = Term::from_field_text(text_field, s);
            searcher.doc_freq(&term_a)
        };

        {
            // writing the segment
            let mut index_writer = index.writer_with_num_threads(3, 40_000_000).unwrap();
            {
                let mut doc = Document::default();
                doc.add_text(text_field, "a");
                index_writer.add_document(doc).unwrap();
            }
            assert_eq!(index_writer.rollback().unwrap(), 0u64);
            assert_eq!(num_docs_containing("a"), 0);

            {
                let mut doc = Document::default();
                doc.add_text(text_field, "b");
                index_writer.add_document(doc).unwrap();
            }
            {
                let mut doc = Document::default();
                doc.add_text(text_field, "c");
                index_writer.add_document(doc).unwrap();
            }
            assert_eq!(index_writer.commit().unwrap(), 2u64);

            assert_eq!(num_docs_containing("a"), 0);
            assert_eq!(num_docs_containing("b"), 1);
            assert_eq!(num_docs_containing("c"), 1);
        }
        index.searcher();
    }

}
<|endoftext|>


<commit_before>extern crate clonablechild;

use clonablechild::{ChildExt, ClonableChild};

use platform_helper::*;

use std::io;
use std::process::{Command, Stdio, ExitStatus};
use std::thread::{self, JoinHandle};
use std::time::Duration;

#[test]
fn wait_for_child_twice() {
    let testee = create_testee(INSTANT_EXIT_COMMAND);

    for _ in 0..2 {
        let exit_status = testee.wait().expect("Expected To be able to wait for child");
        assert!(clean_exit(&exit_status));
    }
}

#[test]
fn killing_dead_child() {
    let testee = create_testee(INSTANT_EXIT_COMMAND);
    testee.wait().expect("Expected To be able to wait for child");

    assert!(testee.kill().is_err());
}

#[test]
fn killing_running_without_wait() {
    let testee = create_testee(LONG_RUNNING_COMMAND);

    assert!(testee.kill().is_ok());
    let exit_status = testee.wait().expect("Expected To be able to wait for child");
    assert!(was_killed(&exit_status));
}

#[test]
fn killing_dead_without_wait() {
    let testee = create_testee(INSTANT_EXIT_COMMAND);
    sleep_one_sec();

    let kill_result = testee.kill();
    #[cfg(unix)]
    assert!(kill_result.is_ok());
    #[cfg(windows)]
    assert!(kill_result.is_err());

    let exit_status = testee.wait().expect("Expected To be able to wait for child");
    assert!(!was_killed(&exit_status));
}

#[test]
fn multiple_wait_long_running_child() {
    let (_, threads) = create_testee_where_many_threads_wait(LONG_RUNNING_COMMAND);

    for thread in threads {
        let thread_result = thread.join().expect("Expected to be able to join thread");
        let exit_status = thread_result.expect("Expected thread to have an Result::Ok");
        assert!(clean_exit(&exit_status));
    }
}

#[test]
fn multiple_wait_and_kill_long_running_child() {
    let (testee, threads) = create_testee_where_many_threads_wait(LONG_RUNNING_COMMAND);
    sleep_one_sec();
    testee.kill().expect("Expected kill Result to be Ok");

    for thread in threads {
        let thread_result = thread.join().expect("Expected to be able to join thread");
        let exit_status = thread_result.expect("Expected thread to have a Result::Ok");
        assert!(was_killed(&exit_status));
    }
}

fn create_testee(command: (&str, &[&str])) -> ClonableChild {
    let child = Command::new(command.0)
        .args(command.1)
        .stdout(Stdio::null())
        .spawn()
        .expect(&format!("Expected to be able to spawn {}", command.0));
    child.into_clonable()
}

fn create_testee_where_many_threads_wait
    (command: (&str, &[&str]))
     -> (ClonableChild, Vec<JoinHandle<io::Result<ExitStatus>>>) {
    let testee = create_testee(command);
    let mut threads = vec![];
    for _ in 0..10 {
        let testee_clone = testee.clone();
        threads.push(thread::spawn(move || testee_clone.wait()));
    }
    (testee, threads)
}

fn sleep_one_sec() {
    thread::sleep(Duration::new(1, 0));
}

fn clean_exit(exit_status: &ExitStatus) -> bool {
    exit_status.success() && (exit_status.code() == Some(0))
}

#[cfg(unix)]
pub mod platform_helper {
    use std::process::ExitStatus;

    pub const LONG_RUNNING_COMMAND: (&'static str, &'static [&'static str]) = ("sleep", &["3"]);

    pub const INSTANT_EXIT_COMMAND: (&'static str, &'static [&'static str]) = ("echo", &[]);

    pub fn was_killed(exit_status: &ExitStatus) -> bool {
        (!exit_status.success()) && (exit_status.code().is_none())
    }
}

#[cfg(windows)]
pub mod platform_helper {
    use std::process::ExitStatus;

    pub const LONG_RUNNING_COMMAND: (&'static str, &'static [&'static str]) =
        ("ping", &["127.0.0.1", "-n", "4"]);

    pub const INSTANT_EXIT_COMMAND: (&'static str, &'static [&'static str]) = ("echo", &[]);

    pub fn was_killed(exit_status: &ExitStatus) -> bool {
        (!exit_status.success()) && (exit_status.code() == Some(1))
    }
}
<commit_msg>Add tests for stdio passing<commit_after>extern crate clonablechild;

use clonablechild::{ChildExt, ClonableChild};

use platform_helper::*;

use std::io::{self, Read};
use std::process::{Command, Stdio, ExitStatus};
use std::thread::{self, JoinHandle};
use std::time::Duration;

#[test]
fn wait_for_child_twice() {
    let testee = create_testee(INSTANT_EXIT_COMMAND);

    for _ in 0..2 {
        let exit_status = testee.wait().expect("Expected To be able to wait for child");
        assert!(clean_exit(&exit_status));
    }
}

#[test]
fn killing_dead_child() {
    let testee = create_testee(INSTANT_EXIT_COMMAND);
    testee.wait().expect("Expected To be able to wait for child");

    assert!(testee.kill().is_err());
}

#[test]
fn killing_running_without_wait() {
    let testee = create_testee(LONG_RUNNING_COMMAND);

    assert!(testee.kill().is_ok());
    let exit_status = testee.wait().expect("Expected To be able to wait for child");
    assert!(was_killed(&exit_status));
}

#[test]
fn killing_dead_without_wait() {
    let testee = create_testee(INSTANT_EXIT_COMMAND);
    sleep_one_sec();

    let kill_result = testee.kill();
    #[cfg(unix)]
    assert!(kill_result.is_ok());
    #[cfg(windows)]
    assert!(kill_result.is_err());

    let exit_status = testee.wait().expect("Expected To be able to wait for child");
    assert!(!was_killed(&exit_status));
}

#[test]
fn multiple_wait_long_running_child() {
    let (_, threads) = create_testee_where_many_threads_wait(LONG_RUNNING_COMMAND);

    for thread in threads {
        let thread_result = thread.join().expect("Expected to be able to join thread");
        let exit_status = thread_result.expect("Expected thread to have an Result::Ok");
        assert!(clean_exit(&exit_status));
    }
}

#[test]
fn multiple_wait_and_kill_long_running_child() {
    let (testee, threads) = create_testee_where_many_threads_wait(LONG_RUNNING_COMMAND);
    sleep_one_sec();
    testee.kill().expect("Expected kill Result to be Ok");

    for thread in threads {
        let thread_result = thread.join().expect("Expected to be able to join thread");
        let exit_status = thread_result.expect("Expected thread to have a Result::Ok");
        assert!(was_killed(&exit_status));
    }
}

#[test]
fn stdio_is_passed_to_cloned_child() {
    let mut testee = create_testee_with_stdout(INSTANT_EXIT_COMMAND);

    let mut stdout = testee.stdout().expect("Expected stdout");
    let mut stderr = testee.stderr().expect("Expected stderr");
    let mut stdout_string = String::new();
    let mut stderr_string = String::new();
    stdout.read_to_string(&mut stdout_string).expect("Expected to read from stdout");
    stderr.read_to_string(&mut stderr_string).expect("Expected to read from stderr");

    assert_eq!("hello\n", stdout_string);
    assert_eq!("", stderr_string);
}

fn create_testee(cmd: (&str, &[&str])) -> ClonableChild {
    create_testee_child(cmd, Stdio::null(), Stdio::null())
}

fn create_testee_with_stdout(cmd: (&str, &[&str])) -> ClonableChild {
    create_testee_child(cmd, Stdio::piped(), Stdio::piped())
}

fn create_testee_child(cmd: (&str, &[&str]), stdout: Stdio, stderr: Stdio) -> ClonableChild {
    let child = Command::new(cmd.0)
        .args(cmd.1)
        .stdout(stdout)
        .stderr(stderr)
        .spawn()
        .expect(&format!("Expected to be able to spawn {}", cmd.0));
    child.into_clonable()
}

fn create_testee_where_many_threads_wait
    (cmd: (&str, &[&str]))
     -> (ClonableChild, Vec<JoinHandle<io::Result<ExitStatus>>>) {
    let testee = create_testee(cmd);
    let mut threads = vec![];
    for _ in 0..10 {
        let testee_clone = testee.clone();
        threads.push(thread::spawn(move || testee_clone.wait()));
    }
    (testee, threads)
}

fn sleep_one_sec() {
    thread::sleep(Duration::new(1, 0));
}

fn clean_exit(exit_status: &ExitStatus) -> bool {
    exit_status.success() && (exit_status.code() == Some(0))
}

#[cfg(unix)]
pub mod platform_helper {
    use std::process::ExitStatus;

    pub const LONG_RUNNING_COMMAND: (&'static str, &'static [&'static str]) = ("sleep", &["3"]);

    pub const INSTANT_EXIT_COMMAND: (&'static str, &'static [&'static str]) = ("echo", &["hello"]);

    pub fn was_killed(exit_status: &ExitStatus) -> bool {
        (!exit_status.success()) && (exit_status.code().is_none())
    }
}

#[cfg(windows)]
pub mod platform_helper {
    use std::process::ExitStatus;

    pub const LONG_RUNNING_COMMAND: (&'static str, &'static [&'static str]) =
        ("ping", &["127.0.0.1", "-n", "4"]);

    pub const INSTANT_EXIT_COMMAND: (&'static str, &'static [&'static str]) = ("echo", &["hello"]);

    pub fn was_killed(exit_status: &ExitStatus) -> bool {
        (!exit_status.success()) && (exit_status.code() == Some(1))
    }
}
<|endoftext|>