<commit_before>mod printer;
mod name_matcher;
mod caseless_name_matcher;
mod logical_matchers;
mod type_matcher;
use std::error::Error;
use std::fs::DirEntry;
use std::cell::RefCell;
use std::io::Write;
use std::rc::Rc;

/// A basic interface that can be used to determine whether a directory entry
/// is what's being searched for. To a first order approximation, find consists
/// of building a chain of Matcher objets, and then walking a directory tree,
/// passing each entry to the chain of Matchers.
pub trait Matcher {
    /// Returns whether the given file matches the object's predicate.
    fn matches(&self, file_info: &DirEntry) -> bool;

    /// Returns whether the matcher has any side-effects. Iff no such matcher
    /// exists in the chain, then the filename will be printed to stdout. While
    /// this is a compile-time fact for most matchers, it's run-time for matchers
    /// that contain a collection of sub-Matchers.
    fn has_side_effects(&self) -> bool;
}

/// Builds a single AndMatcher containing the Matcher objects corresponding
/// to the passed in predicate arguments.
pub fn build_top_level_matcher(args: &[&str],
                               output: Rc<RefCell<Write>>)
                               -> Result<Box<Matcher>, Box<Error>> {
    let mut top_level_matcher = logical_matchers::ListMatcher::new();

    // can't use getopts for a variety or reasons:
    // order of arguments is important
    // arguments can start with + as well as -
    // multiple-character flags don't start with a double dash
    let mut i = 0;
    let mut invert_next_matcher = false;
    while i < args.len() {
        let possible_submatcher = match args[i] {
            "-print" => Some(Box::new(printer::Printer::new(output.clone())) as Box<Matcher>),
            "-true" => Some(Box::new(logical_matchers::TrueMatcher {}) as Box<Matcher>),
            "-false" => Some(Box::new(logical_matchers::FalseMatcher {}) as Box<Matcher>),
            "-name" => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("missing argument to {}", args[i])));
                }
                i += 1;
                Some(Box::new(try!(name_matcher::NameMatcher::new(args[i]
                    .as_ref()))) as Box<Matcher>)
            }
            "-iname" => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("missing argument to {}", args[i])));
                }
                i += 1;
                Some(Box::new(try!(caseless_name_matcher::CaselessNameMatcher::new(args[i]))) as Box<Matcher>)
            }
            "-type" => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("missing argument to {}", args[i])));
                }
                i += 1;
                Some(Box::new(try!(type_matcher::TypeMatcher::new(args[i]))) as Box<Matcher>)
            }
            "-not" | "!" => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("expected an expression after {}", args[i])));
                }
                invert_next_matcher = true;
                None
            }
            "-or" | "-o" => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("expected an expression after {}", args[i])));
                }
                try!(top_level_matcher.new_ored_criterion(args[i]));
                None
            }
            "," => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("expected an expression after {}", args[i])));
                }
                try!(top_level_matcher.new_list_entry());
                None
            }
            _ => return Err(From::from(format!("Unrecognized flag: '{}'", args[i]))),
        };
        if let Some(submatcher) = possible_submatcher {
            if invert_next_matcher {
                top_level_matcher.push(Box::new(logical_matchers::NotMatcher::new(submatcher)));
                invert_next_matcher = false;
            } else {
                top_level_matcher.push(submatcher);
            }
        }
        i += 1;
    }

    if !top_level_matcher.has_side_effects() {
        let mut new_and_matcher = logical_matchers::AndMatcher::new();
        new_and_matcher.push(Box::new(top_level_matcher));
        new_and_matcher.push(Box::new(printer::Printer::new(output)));
        return Ok(Box::new(new_and_matcher));
    }
    Ok(Box::new(top_level_matcher))
}

#[cfg(test)]
mod tests {
    use std::fs::DirEntry;
    use std::cell::RefCell;
    use std::vec::Vec;
    use std::io::Cursor;
    use std::rc::Rc;
    use std::io::Read;

    /// Helper function for tests to get a DirEntry object. directory should
    /// probably be a string starting with "test_data/" (cargo's tests run with
    /// a working directory set to the root findutils folder).
    pub fn get_dir_entry_for(directory: &str, filename: &str) -> DirEntry {
        let dir_entries = ::std::fs::read_dir(directory).unwrap();
        for wrapped_dir_entry in dir_entries {
            let dir_entry = wrapped_dir_entry.unwrap();
            if dir_entry.file_name().to_string_lossy() == filename {
                return dir_entry;
            }
        }
        panic!("Couldn't find {} in {}", directory, filename);
    }

    fn new_output() -> Rc<RefCell<Cursor<Vec<u8>>>> {
        Rc::new(RefCell::new(Cursor::new(Vec::<u8>::new())))
    }

    fn get_output_as_string(output: &RefCell<Cursor<Vec<u8>>>) -> String {
        let mut cursor = output.borrow_mut();
        cursor.set_position(0);
        let mut contents = String::new();
        cursor.read_to_string(&mut contents).unwrap();
        contents
    }

    #[test]
    fn build_top_level_matcher_name() {
        let abbbc_lower = get_dir_entry_for("./test_data/simple", "abbbc");
        let abbbc_upper = get_dir_entry_for("./test_data/simple", "ABBBC");
        let output = new_output();

        let matcher = super::build_top_level_matcher(&["-name", "a*c"], output.clone()).unwrap();

        assert!(matcher.matches(&abbbc_lower));
        assert!(!matcher.matches(&abbbc_upper));
        assert_eq!(get_output_as_string(&output), "./test_data/simple/abbbc\n");
    }

    #[test]
    fn build_top_level_matcher_iname() {
        let abbbc_lower = get_dir_entry_for("./test_data/simple", "abbbc");
        let abbbc_upper = get_dir_entry_for("./test_data/simple", "ABBBC");
        let output = new_output();

        let matcher = super::build_top_level_matcher(&["-iname", "a*c"], output.clone()).unwrap();

        assert!(matcher.matches(&abbbc_lower));
        assert!(matcher.matches(&abbbc_upper));
        assert_eq!(get_output_as_string(&output),
                   "./test_data/simple/abbbc\n./test_data/simple/ABBBC\n");
    }

    #[test]
    fn build_top_level_matcher_not() {
        for arg in &["-not", "!"] {
            let abbbc_lower = get_dir_entry_for("./test_data/simple", "abbbc");
            let output = new_output();

            let matcher = super::build_top_level_matcher(&[arg, "-name", "doesntexist"],
                                                         output.clone())
                .unwrap();

            assert!(matcher.matches(&abbbc_lower));
            assert_eq!(get_output_as_string(&output), "./test_data/simple/abbbc\n");
        }
    }

    #[test]
    fn build_top_level_matcher_not_needs_expression() {
        for arg in &["-not", "!"] {
            let output = new_output();

            if let Err(e) = super::build_top_level_matcher(&[arg], output.clone()) {
                assert!(e.description().contains("expected an expression"));
            } else {
                panic!("parsing arugment lists that end in -not should fail");
            }
        }
    }

    #[test]
    fn build_top_level_matcher_missing_args() {
        for arg in &["-iname", "-name", "-type"] {
            let output = new_output();

            if let Err(e) = super::build_top_level_matcher(&[arg], output.clone()) {
                assert!(e.description().contains("missing argument to"));
                assert!(e.description().contains(arg));
            } else {
                panic!("parsing arugment lists that end in -not should fail");
            }
        }
    }

    #[test]
    fn build_top_level_matcher_or_without_expr1() {
        for arg in &["-or", "-o"] {
            let output = new_output();

            if let Err(e) = super::build_top_level_matcher(&[arg, "-true"], output.clone()) {
                assert!(e.description().contains("you have used a binary operator"));
            } else {
                panic!("parsing arugment list that begins with -or should fail");
            }
        }
    }

    #[test]
    fn build_top_level_matcher_or_without_expr2() {
        for arg in &["-or", "-o"] {
            let output = new_output();

            if let Err(e) = super::build_top_level_matcher(&["-true", arg], output.clone()) {
                assert!(e.description().contains("expected an expression"));
            } else {
                panic!("parsing arugment list that ends with -or should fail");
            }
        }
    }

    #[test]
    fn build_top_level_matcher_or_works() {
        let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
        for args in &[["-true", "-o", "-false"],
                      ["-false", "-o", "-true"],
                      ["-true", "-o", "-true"]] {
            let output = new_output();

            let matcher = super::build_top_level_matcher(args, output.clone()).unwrap();

            assert!(matcher.matches(&abbbc));
            assert_eq!(get_output_as_string(&output), "./test_data/simple/abbbc\n");
        }

        let output = new_output();

        let matcher = super::build_top_level_matcher(&["-false", "-o", "-false"], output.clone())
            .unwrap();

        assert!(!matcher.matches(&abbbc));
        assert_eq!(get_output_as_string(&output), "");
    }

    #[test]
    fn build_top_level_matcher_and_works() {
        let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
        for args in &[["-true", "-false"], ["-false", "-true"], ["-false", "-false"]] {
            let output = new_output();

            let matcher = super::build_top_level_matcher(args, output.clone()).unwrap();

            assert!(!matcher.matches(&abbbc));
            assert_eq!(get_output_as_string(&output), "");
        }

        let output = new_output();

        let matcher = super::build_top_level_matcher(&["-true", "-true"], output.clone()).unwrap();

        assert!(matcher.matches(&abbbc));
        assert_eq!(get_output_as_string(&output), "./test_data/simple/abbbc\n");
    }

    #[test]
    fn build_top_level_matcher_list_works() {
        let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
        let args = ["-true", "-print", "-false", ",", "-print", "-false"];
        let output = new_output();

        let matcher = super::build_top_level_matcher(&args, output.clone()).unwrap();

        // final matcher returns false, so list matcher should too
        assert!(!matcher.matches(&abbbc));
        // two print matchers means doubled output
        assert_eq!(get_output_as_string(&output),
                   "./test_data/simple/abbbc\n./test_data/simple/abbbc\n");
    }

    #[test]
    fn build_top_level_matcher_list_without_expr1() {
        let output = new_output();

        if let Err(e) = super::build_top_level_matcher(&[",", "-true"], output.clone()) {
            assert!(e.description().contains("you have used a binary operator"));
        } else {
            panic!("parsing arugment list that begins with , should fail");
        }

        if let Err(e) = super::build_top_level_matcher(&["-true", "-o", ",", "-true"],
                                                       output.clone()) {
            assert!(e.description().contains("you have used a binary operator"));
        } else {
            panic!("parsing arugment list that contains '-o  ,' should fail");
        }

    }

    #[test]
    fn build_top_level_matcher_list_without_expr2() {
        let output = new_output();

        if let Err(e) = super::build_top_level_matcher(&["-true", ","], output.clone()) {
            assert!(e.description().contains("expected an expression"));
        } else {
            panic!("parsing arugment list that ends with , should fail");
        }
    }
}
<commit_msg>Added support for parentheses<commit_after>mod printer;
mod name_matcher;
mod caseless_name_matcher;
mod logical_matchers;
mod type_matcher;
use std::error::Error;
use std::fs::DirEntry;
use std::cell::RefCell;
use std::io::Write;
use std::rc::Rc;

/// A basic interface that can be used to determine whether a directory entry
/// is what's being searched for. To a first order approximation, find consists
/// of building a chain of Matcher objets, and then walking a directory tree,
/// passing each entry to the chain of Matchers.
pub trait Matcher {
    /// Returns whether the given file matches the object's predicate.
    fn matches(&self, file_info: &DirEntry) -> bool;

    /// Returns whether the matcher has any side-effects. Iff no such matcher
    /// exists in the chain, then the filename will be printed to stdout. While
    /// this is a compile-time fact for most matchers, it's run-time for matchers
    /// that contain a collection of sub-Matchers.
    fn has_side_effects(&self) -> bool;
}

/// Builds a single AndMatcher containing the Matcher objects corresponding
/// to the passed in predicate arguments.
pub fn build_top_level_matcher(args: &[&str],
                               output: Rc<RefCell<Write>>)
                               -> Result<Box<Matcher>, Box<Error>> {
    let (_, top_level_matcher) = try!(build_matcher_tree(args, output.clone(), 0, false));

    // if the matcher doesn't have any side-effects, then we default to printing
    if !top_level_matcher.has_side_effects() {
        let mut new_and_matcher = logical_matchers::AndMatcher::new();
        new_and_matcher.push(top_level_matcher);
        new_and_matcher.push(Box::new(printer::Printer::new(output)));
        return Ok(Box::new(new_and_matcher));
    }
    Ok(top_level_matcher)
}

/// Helper function for build_matcher_tree
fn are_more_expressions(args: &[&str], index: usize) -> bool {
    (index < args.len() - 1) && args[index + 1] != ")"
}

/// The main "translate command-line args into a matcher" function. Will call
/// itself recursively if it encounters an opening bracket. A successful return
/// consits of a tuple containing the new index into the args array to use (if
/// called recursively) and the resulting matcher.
fn build_matcher_tree(args: &[&str],
                      output: Rc<RefCell<Write>>,
                      arg_index: usize,
                      expecting_bracket: bool)
                      -> Result<(usize, Box<Matcher>), Box<Error>> {
    let mut top_level_matcher = logical_matchers::ListMatcher::new();

    // can't use getopts for a variety or reasons:
    // order of arguments is important
    // arguments can start with + as well as -
    // multiple-character flags don't start with a double dash
    let mut i = arg_index;
    let mut invert_next_matcher = false;
    while i < args.len() {
        let possible_submatcher = match args[i] {
            "-print" => Some(Box::new(printer::Printer::new(output.clone())) as Box<Matcher>),
            "-true" => Some(Box::new(logical_matchers::TrueMatcher {}) as Box<Matcher>),
            "-false" => Some(Box::new(logical_matchers::FalseMatcher {}) as Box<Matcher>),
            "-name" => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("missing argument to {}", args[i])));
                }
                i += 1;
                Some(Box::new(try!(name_matcher::NameMatcher::new(args[i]
                    .as_ref()))) as Box<Matcher>)
            }
            "-iname" => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("missing argument to {}", args[i])));
                }
                i += 1;
                Some(Box::new(try!(caseless_name_matcher::CaselessNameMatcher::new(args[i]))) as Box<Matcher>)
            }
            "-type" => {
                if i >= args.len() - 1 {
                    return Err(From::from(format!("missing argument to {}", args[i])));
                }
                i += 1;
                Some(Box::new(try!(type_matcher::TypeMatcher::new(args[i]))) as Box<Matcher>)
            }
            "-not" | "!" => {
                if !are_more_expressions(args, i) {
                    return Err(From::from(format!("expected an expression after {}", args[i])));
                }
                invert_next_matcher = true;
                None
            }
            "-or" | "-o" => {
                if !are_more_expressions(args, i) {
                    return Err(From::from(format!("expected an expression after {}", args[i])));
                }
                try!(top_level_matcher.new_ored_criterion(args[i]));
                None
            }
            "," => {
                if !are_more_expressions(args, i) {
                    return Err(From::from(format!("expected an expression after {}", args[i])));
                }
                try!(top_level_matcher.new_list_entry());
                None
            }
            "(" => {
                let (new_arg_index, sub_matcher) =
                    try!(build_matcher_tree(args, output.clone(), i + 1, true));
                i = new_arg_index;
                Some(sub_matcher)
            }
            ")" => {
                if !expecting_bracket {
                    return Err(From::from("you have too many ')'"));
                }
                return Ok((i, Box::new(top_level_matcher)));
            }

            _ => return Err(From::from(format!("Unrecognized flag: '{}'", args[i]))),
        };
        if let Some(submatcher) = possible_submatcher {
            if invert_next_matcher {
                top_level_matcher.push(Box::new(logical_matchers::NotMatcher::new(submatcher)));
                invert_next_matcher = false;
            } else {
                top_level_matcher.push(submatcher);
            }
        }
        i += 1;
    }
    if expecting_bracket {
        return Err(From::from("invalid expression; I was expecting to find a ')' somewhere but \
                               did not see one."));
    }
    Ok((i, Box::new(top_level_matcher)))
}

#[cfg(test)]
mod tests {
    use std::fs::DirEntry;
    use std::cell::RefCell;
    use std::vec::Vec;
    use std::io::Cursor;
    use std::rc::Rc;
    use std::io::Read;

    /// Helper function for tests to get a DirEntry object. directory should
    /// probably be a string starting with "test_data/" (cargo's tests run with
    /// a working directory set to the root findutils folder).
    pub fn get_dir_entry_for(directory: &str, filename: &str) -> DirEntry {
        let dir_entries = ::std::fs::read_dir(directory).unwrap();
        for wrapped_dir_entry in dir_entries {
            let dir_entry = wrapped_dir_entry.unwrap();
            if dir_entry.file_name().to_string_lossy() == filename {
                return dir_entry;
            }
        }
        panic!("Couldn't find {} in {}", directory, filename);
    }

    fn new_output() -> Rc<RefCell<Cursor<Vec<u8>>>> {
        Rc::new(RefCell::new(Cursor::new(Vec::<u8>::new())))
    }

    fn get_output_as_string(output: &RefCell<Cursor<Vec<u8>>>) -> String {
        let mut cursor = output.borrow_mut();
        cursor.set_position(0);
        let mut contents = String::new();
        cursor.read_to_string(&mut contents).unwrap();
        contents
    }

    #[test]
    fn build_top_level_matcher_name() {
        let abbbc_lower = get_dir_entry_for("./test_data/simple", "abbbc");
        let abbbc_upper = get_dir_entry_for("./test_data/simple", "ABBBC");
        let output = new_output();

        let matcher = super::build_top_level_matcher(&["-name", "a*c"], output.clone()).unwrap();

        assert!(matcher.matches(&abbbc_lower));
        assert!(!matcher.matches(&abbbc_upper));
        assert_eq!(get_output_as_string(&output), "./test_data/simple/abbbc\n");
    }

    #[test]
    fn build_top_level_matcher_iname() {
        let abbbc_lower = get_dir_entry_for("./test_data/simple", "abbbc");
        let abbbc_upper = get_dir_entry_for("./test_data/simple", "ABBBC");
        let output = new_output();

        let matcher = super::build_top_level_matcher(&["-iname", "a*c"], output.clone()).unwrap();

        assert!(matcher.matches(&abbbc_lower));
        assert!(matcher.matches(&abbbc_upper));
        assert_eq!(get_output_as_string(&output),
                   "./test_data/simple/abbbc\n./test_data/simple/ABBBC\n");
    }

    #[test]
    fn build_top_level_matcher_not() {
        for arg in &["-not", "!"] {
            let abbbc_lower = get_dir_entry_for("./test_data/simple", "abbbc");
            let output = new_output();

            let matcher = super::build_top_level_matcher(&[arg, "-name", "doesntexist"],
                                                         output.clone())
                .unwrap();

            assert!(matcher.matches(&abbbc_lower));
            assert_eq!(get_output_as_string(&output), "./test_data/simple/abbbc\n");
        }
    }

    #[test]
    fn build_top_level_matcher_not_needs_expression() {
        for arg in &["-not", "!"] {
            let output = new_output();

            if let Err(e) = super::build_top_level_matcher(&[arg], output.clone()) {
                assert!(e.description().contains("expected an expression"));
            } else {
                panic!("parsing arugment lists that end in -not should fail");
            }
        }
    }

    #[test]
    fn build_top_level_matcher_missing_args() {
        for arg in &["-iname", "-name", "-type"] {
            let output = new_output();

            if let Err(e) = super::build_top_level_matcher(&[arg], output.clone()) {
                assert!(e.description().contains("missing argument to"));
                assert!(e.description().contains(arg));
            } else {
                panic!("parsing arugment lists that end in -not should fail");
            }
        }
    }

    #[test]
    fn build_top_level_matcher_or_without_expr1() {
        for arg in &["-or", "-o"] {
            let output = new_output();

            if let Err(e) = super::build_top_level_matcher(&[arg, "-true"], output.clone()) {
                assert!(e.description().contains("you have used a binary operator"));
            } else {
                panic!("parsing arugment list that begins with -or should fail");
            }
        }
    }

    #[test]
    fn build_top_level_matcher_or_without_expr2() {
        for arg in &["-or", "-o"] {
            let output = new_output();

            if let Err(e) = super::build_top_level_matcher(&["-true", arg], output.clone()) {
                assert!(e.description().contains("expected an expression"));
            } else {
                panic!("parsing arugment list that ends with -or should fail");
            }
        }
    }

    #[test]
    fn build_top_level_matcher_or_works() {
        let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
        for args in &[["-true", "-o", "-false"],
                      ["-false", "-o", "-true"],
                      ["-true", "-o", "-true"]] {
            let output = new_output();

            let matcher = super::build_top_level_matcher(args, output.clone()).unwrap();

            assert!(matcher.matches(&abbbc));
            assert_eq!(get_output_as_string(&output), "./test_data/simple/abbbc\n");
        }

        let output = new_output();

        let matcher = super::build_top_level_matcher(&["-false", "-o", "-false"], output.clone())
            .unwrap();

        assert!(!matcher.matches(&abbbc));
        assert_eq!(get_output_as_string(&output), "");
    }

    #[test]
    fn build_top_level_matcher_and_works() {
        let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
        for args in &[["-true", "-false"], ["-false", "-true"], ["-false", "-false"]] {
            let output = new_output();

            let matcher = super::build_top_level_matcher(args, output.clone()).unwrap();

            assert!(!matcher.matches(&abbbc));
            assert_eq!(get_output_as_string(&output), "");
        }

        let output = new_output();

        let matcher = super::build_top_level_matcher(&["-true", "-true"], output.clone()).unwrap();

        assert!(matcher.matches(&abbbc));
        assert_eq!(get_output_as_string(&output), "./test_data/simple/abbbc\n");
    }

    #[test]
    fn build_top_level_matcher_list_works() {
        let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
        let args = ["-true", "-print", "-false", ",", "-print", "-false"];
        let output = new_output();

        let matcher = super::build_top_level_matcher(&args, output.clone()).unwrap();

        // final matcher returns false, so list matcher should too
        assert!(!matcher.matches(&abbbc));
        // two print matchers means doubled output
        assert_eq!(get_output_as_string(&output),
                   "./test_data/simple/abbbc\n./test_data/simple/abbbc\n");
    }

    #[test]
    fn build_top_level_matcher_list_without_expr1() {
        let output = new_output();

        if let Err(e) = super::build_top_level_matcher(&[",", "-true"], output.clone()) {
            assert!(e.description().contains("you have used a binary operator"));
        } else {
            panic!("parsing arugment list that begins with , should fail");
        }

        if let Err(e) = super::build_top_level_matcher(&["-true", "-o", ",", "-true"],
                                                       output.clone()) {
            assert!(e.description().contains("you have used a binary operator"));
        } else {
            panic!("parsing arugment list that contains '-o  ,' should fail");
        }

    }

    #[test]
    fn build_top_level_matcher_list_without_expr2() {
        let output = new_output();

        if let Err(e) = super::build_top_level_matcher(&["-true", ","], output.clone()) {
            assert!(e.description().contains("expected an expression"));
        } else {
            panic!("parsing arugment list that ends with , should fail");
        }
    }

    #[test]
    fn build_top_level_matcher_not_enough_brackets() {
        let output = new_output();

        if let Err(e) = super::build_top_level_matcher(&["-true", "("], output.clone()) {
            assert!(e.description().contains("I was expecting to find a ')'"));
        } else {
            panic!("parsing arugment list with not enough closing brackets should fail");
        }
    }

    #[test]
    fn build_top_level_matcher_too_many_brackets() {
        let output = new_output();

        if let Err(e) = super::build_top_level_matcher(&["-true", "(", ")", ")"], output.clone()) {
            assert!(e.description().contains("too many ')'"));
        } else {
            panic!("parsing arugment list with too many closing brackets should fail");
        }
    }

    #[test]
    fn build_top_level_matcher_can_use_bracket_as_arg() {
        let output = new_output();
        // make sure that if we use a bracket as an argument (e.g. to -name)
        // then it isn't viewed as a bracket
        super::build_top_level_matcher(&["-name", "("], output.clone()).unwrap();
        super::build_top_level_matcher(&["-name", ")"], output.clone()).unwrap();
    }

    #[test]
    fn build_top_level_matcher_brackets_work() {
        let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
        // same as true | ( false & false) = true
        let args_without = ["-true", "-o", "-false", "-false"];
        // same as (true | false) & false = false
        let args_with = ["(", "-true", "-o", "-false", ")", "-false"];
        let output = new_output();

        {
            let matcher = super::build_top_level_matcher(&args_without, output.clone()).unwrap();
            assert!(matcher.matches(&abbbc));
        }
        {
            let matcher = super::build_top_level_matcher(&args_with, output.clone()).unwrap();
            assert!(!matcher.matches(&abbbc));
        }
    }

    #[test]
    fn build_top_level_matcher_not_and_brackets_work() {
        let abbbc = get_dir_entry_for("./test_data/simple", "abbbc");
        // same as (true & !(false)) | true = true
        let args_without = ["-true", "-not", "-false", "-o", "-true"];
        // same as true & !(false | true) = false
        let args_with = ["-true", "-not", "(", "-false", "-o", "-true", ")"];
        let output = new_output();

        {
            let matcher = super::build_top_level_matcher(&args_without, output.clone()).unwrap();
            assert!(matcher.matches(&abbbc));
        }
        {
            let matcher = super::build_top_level_matcher(&args_with, output.clone()).unwrap();
            assert!(!matcher.matches(&abbbc));
        }
    }

}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashMap};

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::{PathAndJson, ScopedJson};
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

use rhai::{Dynamic, Engine, Scope, AST};

use serde_json::value::{Number, Value as Json};

pub struct ScriptHelper {
    pub(crate) script: AST,
}

#[inline]
fn call_script_helper<'reg: 'rc, 'rc>(
    params: &Vec<PathAndJson<'reg, 'rc>>,
    hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
    engine: &Engine,
    script: &AST,
) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
    let params: Dynamic = params
        .iter()
        .map(|p| to_dynamic(p.value()))
        .collect::<Vec<Dynamic>>()
        .into();
    let hash: Dynamic = hash
        .iter()
        .map(|(k, v)| ((*k).to_owned(), to_dynamic(v.value())))
        .collect::<HashMap<String, Dynamic>>()
        .into();

    let mut scope = Scope::new();
    scope.push_dynamic("params", params);
    scope.push_dynamic("hash", hash);

    let result = engine
        .eval_ast_with_scope::<Dynamic>(&mut scope, script)
        .map_err(RenderError::from)?;

    let result_json = to_json(&result);

    Ok(Some(ScopedJson::Derived(result_json)))
}

impl HelperDef for ScriptHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        reg: &'reg Registry<'reg>,
        _ctx: &'rc Context,
        _rc: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
    }
}

fn to_dynamic(j: &Json) -> Dynamic {
    match j {
        Json::Number(n) => Dynamic::from(n.clone()),
        Json::Bool(b) => Dynamic::from(*b),
        Json::Null => Dynamic::from(()),
        Json::String(s) => Dynamic::from(s.clone()),
        Json::Array(ref v) => {
            let dyn_vec: Vec<Dynamic> = v.iter().map(|i| to_dynamic(i)).collect();
            Dynamic::from(dyn_vec)
        }
        Json::Object(ref o) => {
            let dyn_map: HashMap<String, Dynamic> = o
                .iter()
                .map(|(k, v)| ((*k).to_owned(), to_dynamic(v)))
                .collect();
            Dynamic::from(dyn_map)
        }
    }
}

fn to_json(d: &Dynamic) -> Json {
    if let Ok(s) = d.as_str() {
        return Json::String(s.to_owned());
    }
    if let Ok(i) = d.as_int() {
        return Json::Number(Number::from(i));
    }
    if let Ok(b) = d.as_bool() {
        return Json::Bool(b);
    }

    // FIXME: more types
    return Json::Null;
}

#[cfg(test)]
mod test {
    use super::{call_script_helper, to_dynamic};
    use crate::json::value::{PathAndJson, ScopedJson};
    use rhai::Engine;

    #[test]
    fn test_dynamic_convert() {
        let j0 = json! {
            [{"name": "tomcat"}, {"name": "jetty"}]
        };

        let d0 = to_dynamic(&j0);
        assert_eq!("array", d0.type_name());

        let j1 = json!({
            "name": "tomcat",
            "value": 4000,
        });

        let d1 = to_dynamic(&j1);
        assert_eq!("map", d1.type_name());
    }

    #[test]
    fn test_script_helper_value_access() {
        let engine = Engine::new();

        let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
        let ast = engine.compile(&script).unwrap();

        let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
        let hash = btreemap! {
            "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
            "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
        };

        let result = call_script_helper(&params, &hash, &engine, &ast)
            .unwrap()
            .unwrap()
            .render();
        assert_eq!("1,true,2,no", &result);
    }
}
<commit_msg>(feat) add collection conversion from dynamic to json<commit_after>use std::collections::{BTreeMap, HashMap};

use crate::context::Context;
use crate::error::RenderError;
use crate::helpers::HelperDef;
use crate::json::value::{PathAndJson, ScopedJson};
use crate::registry::Registry;
use crate::render::{Helper, RenderContext};

use rhai::{Dynamic, Engine, Scope, AST};

use serde_json::value::{Map, Number, Value as Json};

pub struct ScriptHelper {
    pub(crate) script: AST,
}

#[inline]
fn call_script_helper<'reg: 'rc, 'rc>(
    params: &Vec<PathAndJson<'reg, 'rc>>,
    hash: &BTreeMap<&'reg str, PathAndJson<'reg, 'rc>>,
    engine: &Engine,
    script: &AST,
) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
    let params: Dynamic = params
        .iter()
        .map(|p| to_dynamic(p.value()))
        .collect::<Vec<Dynamic>>()
        .into();
    let hash: Dynamic = hash
        .iter()
        .map(|(k, v)| ((*k).to_owned(), to_dynamic(v.value())))
        .collect::<HashMap<String, Dynamic>>()
        .into();

    let mut scope = Scope::new();
    scope.push_dynamic("params", params);
    scope.push_dynamic("hash", hash);

    let result = engine
        .eval_ast_with_scope::<Dynamic>(&mut scope, script)
        .map_err(RenderError::from)?;

    let result_json = to_json(&result);

    Ok(Some(ScopedJson::Derived(result_json)))
}

impl HelperDef for ScriptHelper {
    fn call_inner<'reg: 'rc, 'rc>(
        &self,
        h: &Helper<'reg, 'rc>,
        reg: &'reg Registry<'reg>,
        _ctx: &'rc Context,
        _rc: &mut RenderContext<'reg, 'rc>,
    ) -> Result<Option<ScopedJson<'reg, 'rc>>, RenderError> {
        call_script_helper(h.params(), h.hash(), &reg.engine, &self.script)
    }
}

fn to_dynamic(j: &Json) -> Dynamic {
    match j {
        Json::Number(n) => Dynamic::from(n.clone()),
        Json::Bool(b) => Dynamic::from(*b),
        Json::Null => Dynamic::from(()),
        Json::String(s) => Dynamic::from(s.clone()),
        Json::Array(ref v) => {
            let dyn_vec: Vec<Dynamic> = v.iter().map(|i| to_dynamic(i)).collect();
            Dynamic::from(dyn_vec)
        }
        Json::Object(ref o) => {
            let dyn_map: HashMap<String, Dynamic> = o
                .iter()
                .map(|(k, v)| ((*k).to_owned(), to_dynamic(v)))
                .collect();
            Dynamic::from(dyn_map)
        }
    }
}

fn to_json(d: &Dynamic) -> Json {
    if let Ok(s) = d.as_str() {
        return Json::String(s.to_owned());
    }
    if let Ok(i) = d.as_int() {
        return Json::Number(Number::from(i));
    }
    if let Ok(b) = d.as_bool() {
        return Json::Bool(b);
    }

    if d.type_name() == "array" {
        let v = d
            .downcast_ref::<Vec<Dynamic>>()
            .unwrap()
            .iter()
            .map(to_json)
            .collect::<Vec<Json>>();
        return Json::Array(v);
    }

    if d.type_name() == "map" {
        let m = d
            .downcast_ref::<HashMap<String, Dynamic>>()
            .unwrap()
            .iter()
            .map(|(k, v)| (k.clone(), to_json(v)))
            .collect::<Map<String, Json>>();

        return Json::Object(m);
    }

    // FIXME: more types
    return Json::Null;
}

#[cfg(test)]
mod test {
    use super::*;
    use crate::json::value::{PathAndJson, ScopedJson};
    use rhai::Engine;

    #[test]
    fn test_dynamic_convert() {
        let j0 = json! {
            [{"name": "tomcat"}, {"name": "jetty"}]
        };

        let d0 = to_dynamic(&j0);
        assert_eq!("array", d0.type_name());

        let j1 = json!({
            "name": "tomcat",
            "value": 4000,
        });

        let d1 = to_dynamic(&j1);
        assert_eq!("map", d1.type_name());
    }

    #[test]
    fn test_to_json() {
        let d0 = Dynamic::from("tomcat".to_owned());

        assert_eq!(Json::String("tomcat".to_owned()), to_json(&d0));
    }

    #[test]
    fn test_script_helper_value_access() {
        let engine = Engine::new();

        let script = "let plen = len(params); let p0 = params[0]; let hlen = len(hash); let hme = hash[\"me\"]; plen + \",\" + p0 + \",\" + hlen + \",\" + hme";
        let ast = engine.compile(&script).unwrap();

        let params = vec![PathAndJson::new(None, ScopedJson::Derived(json!(true)))];
        let hash = btreemap! {
            "me" => PathAndJson::new(None, ScopedJson::Derived(json!("no"))),
            "you" => PathAndJson::new(None, ScopedJson::Derived(json!("yes"))),
        };

        let result = call_script_helper(&params, &hash, &engine, &ast)
            .unwrap()
            .unwrap()
            .render();
        assert_eq!("1,true,2,no", &result);
    }
}
<|endoftext|>


<commit_before>#![crate_name = "hostname"]
#![feature(collections, core, old_io, rustc_private)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Alan Andrade <alan.andradec@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * Synced with:
 *
 * https://www.opensource.apple.com/source/shell_cmds/shell_cmds-170/hostname/hostname.c?txt
 */

extern crate getopts;
extern crate libc;

use std::collections::hash_set::HashSet;
use std::old_io::net::addrinfo;
use std::iter::repeat;
use std::str;
use getopts::{optflag, getopts, usage};

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "hostname";

extern {
    fn gethostname(name: *mut libc::c_char, namelen: libc::size_t) -> libc::c_int;
}

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
extern {
    fn sethostname(name: *const libc::c_char, namelen: libc::c_int) -> libc::c_int;
}

#[cfg(target_os = "linux")]
extern {
    fn sethostname(name: *const libc::c_char, namelen: libc::size_t) -> libc::c_int;
}

pub fn uumain(args: Vec<String>) -> i32 {
    let program = &args[0];

    let options = [
        optflag("d", "domain", "Display the name of the DNS domain if possible"),
        optflag("i", "ip-address", "Display the network address(es) of the host"),
        optflag("f", "fqdn", "Display the FQDN (Fully Qualified Domain Name) (default)"),   // TODO: support --long
        optflag("s", "short", "Display the short hostname (the portion before the first dot) if possible"),
        optflag("h", "help", "Show help"),
        optflag("V", "version", "Show program's version")
    ];

    let matches = match getopts(args.tail(), &options) {
        Ok(m) => { m }
        _ => { help_menu(program.as_slice(), &options); return 0; }
    };

    if matches.opt_present("h") {
        help_menu(program.as_slice(), &options);
        return 0
    }
    if matches.opt_present("V") { version(); return 0 }

    match matches.free.len() {
        0 => {
            let hostname = xgethostname();

            if matches.opt_present("i") {
                match addrinfo::get_host_addresses(hostname.as_slice()) {
                    Ok(addresses) => {
                        let mut hashset = HashSet::new();
                        let mut output = String::new();
                        for addr in addresses.iter() {
                            // XXX: not sure why this is necessary...
                            if !hashset.contains(addr) {
                                output.push_str(addr.to_string().as_slice());
                                output.push_str(" ");
                                hashset.insert(addr.clone());
                            }
                        }
                        let len = output.len();
                        if len > 0 {
                            println!("{}", output.as_slice().slice_to(len - 1));
                        }
                    }
                    Err(f) => {
                        show_error!("{}", f);
                        return 1;
                    }
                }
            } else {
                if matches.opt_present("s") {
                    let pos = hostname.as_slice().find_str(".");
                    if pos.is_some() {
                        println!("{}", hostname.as_slice().slice_to(pos.unwrap()));
                        return 0;
                    }
                } else if matches.opt_present("d") {
                    let pos = hostname.as_slice().find_str(".");
                    if pos.is_some() {
                        println!("{}", hostname.as_slice().slice_from(pos.unwrap() + 1));
                        return 0;
                    }
                }

                println!("{}", hostname);
            }
        }
        1 => xsethostname(matches.free.last().unwrap().as_slice()),
        _ => help_menu(program.as_slice(), &options)
    };

    0
}

fn version() {
    println!("hostname 1.0.0");
}

fn help_menu(program: &str, options: &[getopts::OptGroup]) {
    version();
    println!("");
    println!("Usage:");
    println!("  {} [OPTION]... [HOSTNAME]", program);
    println!("");
    print!("{}", usage("Print or set the system's host name.", options));
}

fn xgethostname() -> String {
    let namelen = 256usize;
    let mut name : Vec<u8> = repeat(0).take(namelen).collect();
    let err = unsafe {
        gethostname (name.as_mut_ptr() as *mut libc::c_char,
                                        namelen as libc::size_t)
    };

    if err != 0 {
        panic!("Cannot determine hostname");
    }

    let last_char = name.iter().position(|byte| *byte == 0).unwrap_or(namelen);

    str::from_utf8(&name[..last_char]).unwrap().to_string()
}

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
fn xsethostname(name: &str) {
    let vec_name: Vec<libc::c_char> = name.bytes().map(|c| c as libc::c_char).collect();

    let err = unsafe {
        sethostname (vec_name.as_ptr(), vec_name.len() as libc::c_int)
    };

    if err != 0 {
        println!("Cannot set hostname to {}", name);
    }
}

#[cfg(target_os = "linux")]
fn xsethostname(name: &str) {
    let vec_name: Vec<libc::c_char> = name.bytes().map(|c| c as libc::c_char).collect();

    let err = unsafe {
        sethostname (vec_name.as_ptr(), vec_name.len() as libc::size_t)
    };

    if err != 0 {
        println!("Cannot set hostname to {}", name);
    }
}
<commit_msg>Fix hostname<commit_after>#![crate_name = "hostname"]
#![feature(collections)]

/*
 * This file is part of the uutils coreutils package.
 *
 * (c) Alan Andrade <alan.andradec@gmail.com>
 *
 * For the full copyright and license information, please view the LICENSE
 * file that was distributed with this source code.
 *
 * Synced with:
 *
 * https://www.opensource.apple.com/source/shell_cmds/shell_cmds-170/hostname/hostname.c?txt
 */

extern crate getopts;
extern crate libc;

use std::collections::hash_set::HashSet;
use std::iter::repeat;
use std::str;
use std::io::Write;
use std::net::ToSocketAddrs;
use getopts::Options;

#[path = "../common/util.rs"]
#[macro_use]
mod util;

static NAME: &'static str = "hostname";

extern {
    fn gethostname(name: *mut libc::c_char, namelen: libc::size_t) -> libc::c_int;
}

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
extern {
    fn sethostname(name: *const libc::c_char, namelen: libc::c_int) -> libc::c_int;
}

#[cfg(target_os = "linux")]
extern {
    fn sethostname(name: *const libc::c_char, namelen: libc::size_t) -> libc::c_int;
}

pub fn uumain(args: Vec<String>) -> i32 {
    let program = &args[0];

    let mut opts = Options::new();
    opts.optflag("d", "domain", "Display the name of the DNS domain if possible");
    opts.optflag("i", "ip-address", "Display the network address(es) of the host");
    opts.optflag("f", "fqdn", "Display the FQDN (Fully Qualified Domain Name) (default)");   // TODO: support --long
    opts.optflag("s", "short", "Display the short hostname (the portion before the first dot) if possible");
    opts.optflag("h", "help", "Show help");
    opts.optflag("V", "version", "Show program's version");

    let matches = match opts.parse(args.tail()) {
        Ok(m) => { m }
        _ => { help_menu(program, opts); return 0; }
    };

    if matches.opt_present("h") {
        help_menu(program, opts);
        return 0
    }
    if matches.opt_present("V") { version(); return 0 }

    match matches.free.len() {
        0 => {
            let hostname = xgethostname();

            if matches.opt_present("i") {
                match hostname.to_socket_addrs() {
                    Ok(addresses) => {
                        let mut hashset = HashSet::new();
                        let mut output = String::new();
                        for addr in addresses {
                            // XXX: not sure why this is necessary...
                            if !hashset.contains(&addr) {
                                output.push_str(&format!("{}", addr));
                                output.push_str(" ");
                                hashset.insert(addr.clone());
                            }
                        }
                        let len = output.len();
                        if len > 0 {
                            println!("{}", &output[0 .. len - 1]);
                        }
                    }
                    Err(f) => {
                        show_error!("{}", f);
                        return 1;
                    }
                }
            } else {
                if matches.opt_present("s") {
                    let mut it = hostname.char_indices().filter(|&ci| ci.1 == '.');
                    let ci = it.next();
                    if ci.is_some() {
                        println!("{}", &hostname[0 .. ci.unwrap().0]);
                        return 0;
                    }
                } else if matches.opt_present("d") {
                    let mut it = hostname.char_indices().filter(|&ci| ci.1 == '.');
                    let ci = it.next();
                    if ci.is_some() {
                        println!("{}", &hostname[ci.unwrap().0 + 1 .. ]);
                        return 0;
                    }
                }

                println!("{}", hostname);
            }
        }
        1 => xsethostname(matches.free.last().unwrap()),
        _ => help_menu(program, opts)
    };

    0
}

fn version() {
    println!("hostname 1.0.0");
}

fn help_menu(program: &str, options: Options) {
    version();
    println!("");
    println!("Usage:");
    println!("  {} [OPTION]... [HOSTNAME]", program);
    println!("");
    print!("{}", options.usage("Print or set the system's host name."));
}

fn xgethostname() -> String {
    let namelen = 256usize;
    let mut name : Vec<u8> = repeat(0).take(namelen).collect();
    let err = unsafe {
        gethostname (name.as_mut_ptr() as *mut libc::c_char,
                                        namelen as libc::size_t)
    };

    if err != 0 {
        panic!("Cannot determine hostname");
    }

    let last_char = name.iter().position(|byte| *byte == 0).unwrap_or(namelen);

    str::from_utf8(&name[..last_char]).unwrap().to_string()
}

#[cfg(any(target_os = "macos", target_os = "freebsd"))]
fn xsethostname(name: &str) {
    let vec_name: Vec<libc::c_char> = name.bytes().map(|c| c as libc::c_char).collect();

    let err = unsafe {
        sethostname (vec_name.as_ptr(), vec_name.len() as libc::c_int)
    };

    if err != 0 {
        println!("Cannot set hostname to {}", name);
    }
}

#[cfg(target_os = "linux")]
fn xsethostname(name: &str) {
    let vec_name: Vec<libc::c_char> = name.bytes().map(|c| c as libc::c_char).collect();

    let err = unsafe {
        sethostname (vec_name.as_ptr(), vec_name.len() as libc::size_t)
    };

    if err != 0 {
        println!("Cannot set hostname to {}", name);
    }
}
<|endoftext|>


<commit_before><commit_msg>Predictive break<commit_after><|endoftext|>


<commit_before>#![cfg(target_os="android")]
#![cfg(feature = "oculusvr")]

use {VRService, VRDisplay, VRDisplayPtr, VREvent, VRGamepadPtr};
use android_injected_glue as android;
use android_injected_glue::ffi as ndk;
use ovr_mobile_sys as ovr;
use std::mem;
use std::ptr;
use super::display::{OculusVRDisplay, OculusVRDisplayPtr};
use super::gamepad::{OculusVRGamepad, OculusVRGamepadPtr};
use super::jni_utils::JNIScope;

const SERVICE_CLASS_NAME:&'static str = "com/rust/webvr/OVRService";

pub struct OculusVRService {
    initialized: bool,
    ovr_java: ovr::ovrJava,
    displays: Vec<OculusVRDisplayPtr>,
    gamepads: Vec<OculusVRGamepadPtr>,
    java_object: ndk::jobject,
    java_class: ndk::jclass,
}

unsafe impl Send for OculusVRService {}

impl VRService for OculusVRService {
    fn initialize(&mut self) -> Result<(), String> {
        if self.is_initialized() {
            return Ok(());
        }

        unsafe {
            try!(self.api_init());
            //self.create_controller_context();
        }

        Ok(())
    }

    fn fetch_displays(&mut self) -> Result<Vec<VRDisplayPtr>,String> {
        // Return cached displays if available
        if self.is_initialized() && self.displays.len() > 0 {
            return Ok(self.clone_displays());
        }

        // Ensure that there are not initialization errors
        try!(self.initialize());
        let display = unsafe { OculusVRDisplay::new(&self.ovr_java as *const _) };
        self.displays.push(display);

        Ok(self.clone_displays())
    }

    fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> {
        // Return cached gamepads if available
        if self.is_initialized() && self.gamepads.len() > 0 {
            return Ok(self.clone_gamepads());
        }
        try!(self.initialize());

        let gamepad = unsafe {
            let display_id = match self.displays.first() {
                Some(display) => display.borrow().id(),
                None => 0
            };
            try!(OculusVRGamepad::new(display_id))
        };
        self.gamepads.push(gamepad);

        Ok(self.clone_gamepads())
    }

    fn is_available(&self) -> bool {
        true
    }

    fn poll_events(&self) -> Vec<VREvent> {
        Vec::new()
    }
}

impl OculusVRService {
    pub fn new() -> OculusVRService {
        OculusVRService {
            initialized: false,
            ovr_java: unsafe { mem::zeroed() },
            displays: Vec::new(),
            gamepads: Vec::new(),
            java_object: ptr::null_mut(),
            java_class: ptr::null_mut(),
        }
    }

    fn is_initialized(&self) -> bool {
        self.initialized
    }

    unsafe fn api_init(&mut self) -> Result<(), String> {
        let jni_scope = try!(JNIScope::attach());

        let jni = jni_scope.jni;
        let env = jni_scope.env;

        // Use NativeActivity's classloader to find our class
        self.java_class = try!(jni_scope.find_class(SERVICE_CLASS_NAME));
        if self.java_class.is_null() {
            return Err("Didn't find GVRService class".into());
        };
        self.java_class = (jni.NewGlobalRef)(env, self.java_class);

        // Create OVRService instance and own it as a globalRef.
        let method = jni_scope.get_method(self.java_class, "create", "(Landroid/app/Activity;J)Ljava/lang/Object;", true);
        let thiz: usize = mem::transmute(self as * mut OculusVRService);
        self.java_object = (jni.CallStaticObjectMethod)(env, self.java_class, method, jni_scope.activity, thiz as ndk::jlong);
        if self.java_object.is_null() {
            return Err("Failed to create OVRService instance".into());
        };
        self.java_object = (jni.NewGlobalRef)(env, self.java_object);

        // Initialize native VrApi
        let activity: &ndk::ANativeActivity = mem::transmute(android::get_app().activity);

        self.ovr_java.Vm = mem::transmute(&mut (*activity.vm).functions);
        self.ovr_java.Env = mem::transmute(&mut (*activity.env).functions);
        self.ovr_java.ActivityObject = activity.clazz as *mut _;

        let init_params = ovr::helpers::vrapi_DefaultInitParms(&self.ovr_java);
        let status = ovr::vrapi_Initialize(&init_params);

        if status == ovr::ovrInitializeStatus::VRAPI_INITIALIZE_SUCCESS {
            self.initialized = true;
            Ok(())
        } else {
            Err(format!("OVR failed to initialize: {:?}", status))
        }
    }

    fn clone_displays(&self) -> Vec<VRDisplayPtr> {
        self.displays.iter().map(|d| d.clone() as VRDisplayPtr).collect()
    }

    fn clone_gamepads(&self) -> Vec<VRGamepadPtr> {
        self.gamepads.iter().map(|d| d.clone() as VRGamepadPtr).collect()
    }

    // Called from Java main thread
    // Pause & resume methods are thread safe
    fn on_pause(&mut self) {
        for display in &self.displays {
            unsafe {
                (*display.as_ptr()).pause();
            }
        }

        for gamepad in &self.gamepads {
            unsafe {
                (*gamepad.as_ptr()).pause();
            }
        }
    }

    // Called from Java main thread
    // Pause & resume methods are thread safe
    fn on_resume(&mut self) {
        for display in &self.displays {
            unsafe {
                (*display.as_ptr()).resume();
            }
        }
        for gamepad in &self.gamepads {
            unsafe {
                (*gamepad.as_ptr()).resume();
            }
        }
    }
}

impl Drop for OculusVRService {
    fn drop(&mut self) {
    }
}

#[cfg(target_os="android")]
#[no_mangle]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub extern fn Java_com_rust_webvr_OVRService_nativeOnPause(_: *mut ndk::JNIEnv, service: ndk::jlong) {
    unsafe {
        let service: *mut OculusVRService = mem::transmute(service as usize);
        (*service).on_pause();
    }
}

#[cfg(target_os="android")]
#[no_mangle]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub extern fn Java_com_rust_webvr_OVRService_nativeOnResume(_: *mut ndk::JNIEnv, service: ndk::jlong) {
    unsafe {
        let service: *mut OculusVRService = mem::transmute(service as usize);
        (*service).on_resume();
    }
}
<commit_msg>Fix JNI threading error<commit_after>#![cfg(target_os="android")]
#![cfg(feature = "oculusvr")]

use {VRService, VRDisplay, VRDisplayPtr, VREvent, VRGamepadPtr};
use android_injected_glue as android;
use android_injected_glue::ffi as ndk;
use ovr_mobile_sys as ovr;
use std::mem;
use std::ptr;
use super::display::{OculusVRDisplay, OculusVRDisplayPtr};
use super::gamepad::{OculusVRGamepad, OculusVRGamepadPtr};
use super::jni_utils::JNIScope;

const SERVICE_CLASS_NAME:&'static str = "com/rust/webvr/OVRService";

pub struct OculusVRService {
    initialized: bool,
    ovr_java: ovr::ovrJava,
    displays: Vec<OculusVRDisplayPtr>,
    gamepads: Vec<OculusVRGamepadPtr>,
    java_object: ndk::jobject,
    java_class: ndk::jclass,
}

unsafe impl Send for OculusVRService {}

impl VRService for OculusVRService {
    fn initialize(&mut self) -> Result<(), String> {
        if self.is_initialized() {
            return Ok(());
        }

        unsafe {
            try!(self.api_init());
            //self.create_controller_context();
        }

        Ok(())
    }

    fn fetch_displays(&mut self) -> Result<Vec<VRDisplayPtr>,String> {
        // Return cached displays if available
        if self.is_initialized() && self.displays.len() > 0 {
            return Ok(self.clone_displays());
        }

        // Ensure that there are not initialization errors
        try!(self.initialize());
        let display = unsafe { OculusVRDisplay::new(&self.ovr_java as *const _) };
        self.displays.push(display);

        Ok(self.clone_displays())
    }

    fn fetch_gamepads(&mut self) -> Result<Vec<VRGamepadPtr>,String> {
        // Return cached gamepads if available
        if self.is_initialized() && self.gamepads.len() > 0 {
            return Ok(self.clone_gamepads());
        }
        try!(self.initialize());

        let gamepad = unsafe {
            let display_id = match self.displays.first() {
                Some(display) => display.borrow().id(),
                None => 0
            };
            try!(OculusVRGamepad::new(display_id))
        };
        self.gamepads.push(gamepad);

        Ok(self.clone_gamepads())
    }

    fn is_available(&self) -> bool {
        true
    }

    fn poll_events(&self) -> Vec<VREvent> {
        Vec::new()
    }
}

impl OculusVRService {
    pub fn new() -> OculusVRService {
        OculusVRService {
            initialized: false,
            ovr_java: unsafe { mem::zeroed() },
            displays: Vec::new(),
            gamepads: Vec::new(),
            java_object: ptr::null_mut(),
            java_class: ptr::null_mut(),
        }
    }

    fn is_initialized(&self) -> bool {
        self.initialized
    }

    unsafe fn api_init(&mut self) -> Result<(), String> {
        let jni_scope = try!(JNIScope::attach());

        let jni = jni_scope.jni;
        let env = jni_scope.env;

        // Use NativeActivity's classloader to find our class
        self.java_class = try!(jni_scope.find_class(SERVICE_CLASS_NAME));
        if self.java_class.is_null() {
            return Err("Didn't find GVRService class".into());
        };
        self.java_class = (jni.NewGlobalRef)(env, self.java_class);

        // Create OVRService instance and own it as a globalRef.
        let method = jni_scope.get_method(self.java_class, "create", "(Landroid/app/Activity;J)Ljava/lang/Object;", true);
        let thiz: usize = mem::transmute(self as * mut OculusVRService);
        self.java_object = (jni.CallStaticObjectMethod)(env, self.java_class, method, jni_scope.activity, thiz as ndk::jlong);
        if self.java_object.is_null() {
            return Err("Failed to create OVRService instance".into());
        };
        self.java_object = (jni.NewGlobalRef)(env, self.java_object);

        // Initialize native VrApi
        let activity: &ndk::ANativeActivity = mem::transmute(android::get_app().activity);

        self.ovr_java.Vm = mem::transmute(&mut (*jni_scope.vm).functions);
        self.ovr_java.Env = mem::transmute(&mut (*env).functions);
        self.ovr_java.ActivityObject = (jni.NewGlobalRef)(env, jni_scope.activity) as *mut _;

        let init_params = ovr::helpers::vrapi_DefaultInitParms(&self.ovr_java);
        let status = ovr::vrapi_Initialize(&init_params);

        if status == ovr::ovrInitializeStatus::VRAPI_INITIALIZE_SUCCESS {
            self.initialized = true;
            Ok(())
        } else {
            Err(format!("OVR failed to initialize: {:?}", status))
        }
    }

    fn clone_displays(&self) -> Vec<VRDisplayPtr> {
        self.displays.iter().map(|d| d.clone() as VRDisplayPtr).collect()
    }

    fn clone_gamepads(&self) -> Vec<VRGamepadPtr> {
        self.gamepads.iter().map(|d| d.clone() as VRGamepadPtr).collect()
    }

    // Called from Java main thread
    // Pause & resume methods are thread safe
    fn on_pause(&mut self) {
        for display in &self.displays {
            unsafe {
                (*display.as_ptr()).pause();
            }
        }

        for gamepad in &self.gamepads {
            unsafe {
                (*gamepad.as_ptr()).pause();
            }
        }
    }

    // Called from Java main thread
    // Pause & resume methods are thread safe
    fn on_resume(&mut self) {
        for display in &self.displays {
            unsafe {
                (*display.as_ptr()).resume();
            }
        }
        for gamepad in &self.gamepads {
            unsafe {
                (*gamepad.as_ptr()).resume();
            }
        }
    }
}

impl Drop for OculusVRService {
    fn drop(&mut self) {
    }
}

#[cfg(target_os="android")]
#[no_mangle]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub extern fn Java_com_rust_webvr_OVRService_nativeOnPause(_: *mut ndk::JNIEnv, service: ndk::jlong) {
    unsafe {
        let service: *mut OculusVRService = mem::transmute(service as usize);
        (*service).on_pause();
    }
}

#[cfg(target_os="android")]
#[no_mangle]
#[allow(non_snake_case)]
#[allow(dead_code)]
pub extern fn Java_com_rust_webvr_OVRService_nativeOnResume(_: *mut ndk::JNIEnv, service: ndk::jlong) {
    unsafe {
        let service: *mut OculusVRService = mem::transmute(service as usize);
        (*service).on_resume();
    }
}
<|endoftext|>


<commit_before>extern crate serde;
extern crate toml;
#[macro_use]
extern crate serde_derive;

use std::collections::{BTreeMap, HashSet};
use serde::{Deserialize, Deserializer};

use toml::Value;
use toml::Value::{Table, Integer, Array, Float};

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

macro_rules! equivalent {
    ($literal:expr, $toml:expr,) => ({
        let toml = $toml;
        let literal = $literal;

        // In/out of Value is equivalent
        println!("try_from");
        assert_eq!(t!(Value::try_from(literal.clone())), toml);
        println!("try_into");
        assert_eq!(literal, t!(toml.clone().try_into()));

        // Through a string equivalent
        println!("to_string(literal)");
        assert_eq!(t!(toml::to_string(&literal)), toml.to_string());
        println!("to_string(toml)");
        assert_eq!(t!(toml::to_string(&toml)), toml.to_string());
        println!("literal, from_str(toml)");
        assert_eq!(literal, t!(toml::from_str(&toml.to_string())));
        println!("toml, from_str(toml)");
        assert_eq!(toml, t!(toml::from_str(&toml.to_string())));
    })
}

macro_rules! error {
    ($ty:ty, $toml:expr, $error:expr) => ({
        println!("attempting parsing");
        match toml::from_str::<$ty>(&$toml.to_string()) {
            Ok(_) => panic!("successful"),
            Err(e) => {
                assert!(e.to_string().contains($error),
                        "bad error: {}", e);
            }
        }

        println!("attempting toml decoding");
        match $toml.try_into::<$ty>() {
            Ok(_) => panic!("successful"),
            Err(e) => {
                assert!(e.to_string().contains($error),
                        "bad error: {}", e);
            }
        }
    })
}

macro_rules! decode( ($t:expr) => ({
    t!($t.try_into())
}) );

macro_rules! map( ($($k:ident: $v:expr),*) => ({
    let mut _m = BTreeMap::new();
    $(_m.insert(stringify!($k).to_string(), $v);)*
    _m
}) );

#[test]
fn smoke() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: isize }

    equivalent!(
        Foo { a: 2 },
        Table(map! { a: Integer(2) }),
    );
}

#[test]
fn smoke_hyphen() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo {
        a_b: isize,
    }

    equivalent! {
        Foo { a_b: 2 },
        Table(map! { a_b: Integer(2) }),
    }

    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo2 {
        #[serde(rename = "a-b")]
        a_b: isize,
    }

    let mut m = BTreeMap::new();
    m.insert("a-b".to_string(), Integer(2));
    equivalent! {
        Foo2 { a_b: 2 },
        Table(m),
    }
}

#[test]
fn nested() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: isize, b: Bar }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar { a: String }

    equivalent! {
        Foo { a: 2, b: Bar { a: "test".to_string() } },
        Table(map! {
            a: Integer(2),
            b: Table(map! {
                a: Value::String("test".to_string())
            })
        }),
    }
}

#[test]
fn application_decode_error() {
    #[derive(PartialEq, Debug)]
    struct Range10(usize);
    impl<'de> Deserialize<'de> for Range10 {
         fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Range10, D::Error> {
             let x: usize = try!(Deserialize::deserialize(d));
             if x > 10 {
                 Err(serde::de::Error::custom("more than 10"))
             } else {
                 Ok(Range10(x))
             }
         }
    }
    let d_good = Integer(5);
    let d_bad1 = Value::String("not an isize".to_string());
    let d_bad2 = Integer(11);

    assert_eq!(Range10(5), d_good.try_into().unwrap());

    let err1: Result<Range10, _> = d_bad1.try_into();
    assert!(err1.is_err());
    let err2: Result<Range10, _> = d_bad2.try_into();
    assert!(err2.is_err());
}

#[test]
fn array() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Vec<isize> }

    equivalent! {
        Foo { a: vec![1, 2, 3, 4] },
        Table(map! {
            a: Array(vec![
                Integer(1),
                Integer(2),
                Integer(3),
                Integer(4)
            ])
        }),
    };
}

#[test]
fn inner_structs_with_options() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo {
        a: Option<Box<Foo>>,
        b: Bar,
    }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar {
        a: String,
        b: f64,
    }

    equivalent! {
        Foo {
            a: Some(Box::new(Foo {
                a: None,
                b: Bar { a: "foo".to_string(), b: 4.5 },
            })),
            b: Bar { a: "bar".to_string(), b: 1.0 },
        },
        Table(map! {
            a: Table(map! {
                b: Table(map! {
                    a: Value::String("foo".to_string()),
                    b: Float(4.5)
                })
            }),
            b: Table(map! {
                a: Value::String("bar".to_string()),
                b: Float(1.0)
            })
        }),
    }
}

#[test]
fn hashmap() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo {
        set: HashSet<char>,
        map: BTreeMap<String, isize>,
    }

    equivalent! {
        Foo {
            map: {
                let mut m = BTreeMap::new();
                m.insert("foo".to_string(), 10);
                m.insert("bar".to_string(), 4);
                m
            },
            set: {
                let mut s = HashSet::new();
                s.insert('a');
                s
            },
        },
        Table(map! {
            map: Table(map! {
                foo: Integer(10),
                bar: Integer(4)
            }),
            set: Array(vec![Value::String("a".to_string())])
        }),
    }
}

#[test]
fn table_array() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Vec<Bar>, }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar { a: isize }

    equivalent! {
        Foo { a: vec![Bar { a: 1 }, Bar { a: 2 }] },
        Table(map! {
            a: Array(vec![
                Table(map!{ a: Integer(1) }),
                Table(map!{ a: Integer(2) }),
            ])
        }),
    }
}

#[test]
fn type_errors() {
    #[derive(Deserialize)]
    #[allow(dead_code)]
    struct Foo { bar: isize }

    error! {
        Foo,
        Table(map! {
            bar: Value::String("a".to_string())
        }),
        "invalid type: string \"a\", expected isize for key `bar`"
    }

    #[derive(Deserialize)]
    #[allow(dead_code)]
    struct Bar { foo: Foo }

    error! {
        Bar,
        Table(map! {
            foo: Table(map! {
                bar: Value::String("a".to_string())
            })
        }),
        "invalid type: string \"a\", expected isize for key `foo.bar`"
    }
}

#[test]
fn missing_errors() {
    #[derive(Serialize, Deserialize, PartialEq, Debug)]
    struct Foo { bar: isize }

    error! {
        Foo,
        Table(map! { }),
        "missing field `bar`"
    }
}

#[test]
fn parse_enum() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: E }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    #[serde(untagged)]
    enum E {
        Bar(isize),
        Baz(String),
        Last(Foo2),
    }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo2 {
        test: String,
    }

    equivalent! {
        Foo { a: E::Bar(10) },
        Table(map! { a: Integer(10) }),
    }

    equivalent! {
        Foo { a: E::Baz("foo".to_string()) },
        Table(map! { a: Value::String("foo".to_string()) }),
    }

    equivalent! {
        Foo { a: E::Last(Foo2 { test: "test".to_string() }) },
        Table(map! { a: Table(map! { test: Value::String("test".to_string()) }) }),
    }
}

#[test]
fn parse_enum_string() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Sort }

    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    #[serde(rename_all = "lowercase")]
    enum Sort {
        Asc,
        Desc,
    }

    equivalent! {
        Foo { a: Sort::Desc },
        Table(map! { a: Value::String("desc".to_string()) }),
    }

}

// #[test]
// fn unused_fields() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: isize }
//
//     let v = Foo { a: 2 };
//     let mut d = Decoder::new(Table(map! {
//         a, Integer(2),
//         b, Integer(5)
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, Some(Table(map! {
//         b, Integer(5)
//     })));
// }
//
// #[test]
// fn unused_fields2() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Bar }
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Bar { a: isize }
//
//     let v = Foo { a: Bar { a: 2 } };
//     let mut d = Decoder::new(Table(map! {
//         a, Table(map! {
//             a, Integer(2),
//             b, Integer(5)
//         })
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, Some(Table(map! {
//         a, Table(map! {
//             b, Integer(5)
//         })
//     })));
// }
//
// #[test]
// fn unused_fields3() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Bar }
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Bar { a: isize }
//
//     let v = Foo { a: Bar { a: 2 } };
//     let mut d = Decoder::new(Table(map! {
//         a, Table(map! {
//             a, Integer(2)
//         })
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields4() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: BTreeMap<String, String> }
//
//     let v = Foo { a: map! { a, "foo".to_string() } };
//     let mut d = Decoder::new(Table(map! {
//         a, Table(map! {
//             a, Value::String("foo".to_string())
//         })
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields5() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Vec<String> }
//
//     let v = Foo { a: vec!["a".to_string()] };
//     let mut d = Decoder::new(Table(map! {
//         a, Array(vec![Value::String("a".to_string())])
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields6() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Option<Vec<String>> }
//
//     let v = Foo { a: Some(vec![]) };
//     let mut d = Decoder::new(Table(map! {
//         a, Array(vec![])
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields7() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Vec<Bar> }
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Bar { a: isize }
//
//     let v = Foo { a: vec![Bar { a: 1 }] };
//     let mut d = Decoder::new(Table(map! {
//         a, Array(vec![Table(map! {
//             a, Integer(1),
//             b, Integer(2)
//         })])
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, Some(Table(map! {
//         a, Array(vec![Table(map! {
//             b, Integer(2)
//         })])
//     })));
// }

#[test]
fn empty_arrays() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Vec<Bar> }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar;

    equivalent! {
        Foo { a: vec![] },
        Table(map! {a: Array(Vec::new())}),
    }
}

#[test]
fn empty_arrays2() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Option<Vec<Bar>> }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar;

    equivalent! {
        Foo { a: None },
        Table(map! {}),
    }

    equivalent!{
        Foo { a: Some(vec![]) },
        Table(map! { a: Array(vec![]) }),
    }
}

#[test]
fn extra_keys() {
    #[derive(Serialize, Deserialize)]
    struct Foo { a: isize }

    let toml = Table(map! { a: Integer(2), b: Integer(2) });
    assert!(toml.clone().try_into::<Foo>().is_ok());
    assert!(toml::from_str::<Foo>(&toml.to_string()).is_ok());
}
<commit_msg>Remove unused test macro<commit_after>extern crate serde;
extern crate toml;
#[macro_use]
extern crate serde_derive;

use std::collections::{BTreeMap, HashSet};
use serde::{Deserialize, Deserializer};

use toml::Value;
use toml::Value::{Table, Integer, Array, Float};

macro_rules! t {
    ($e:expr) => (match $e {
        Ok(t) => t,
        Err(e) => panic!("{} failed with {}", stringify!($e), e),
    })
}

macro_rules! equivalent {
    ($literal:expr, $toml:expr,) => ({
        let toml = $toml;
        let literal = $literal;

        // In/out of Value is equivalent
        println!("try_from");
        assert_eq!(t!(Value::try_from(literal.clone())), toml);
        println!("try_into");
        assert_eq!(literal, t!(toml.clone().try_into()));

        // Through a string equivalent
        println!("to_string(literal)");
        assert_eq!(t!(toml::to_string(&literal)), toml.to_string());
        println!("to_string(toml)");
        assert_eq!(t!(toml::to_string(&toml)), toml.to_string());
        println!("literal, from_str(toml)");
        assert_eq!(literal, t!(toml::from_str(&toml.to_string())));
        println!("toml, from_str(toml)");
        assert_eq!(toml, t!(toml::from_str(&toml.to_string())));
    })
}

macro_rules! error {
    ($ty:ty, $toml:expr, $error:expr) => ({
        println!("attempting parsing");
        match toml::from_str::<$ty>(&$toml.to_string()) {
            Ok(_) => panic!("successful"),
            Err(e) => {
                assert!(e.to_string().contains($error),
                        "bad error: {}", e);
            }
        }

        println!("attempting toml decoding");
        match $toml.try_into::<$ty>() {
            Ok(_) => panic!("successful"),
            Err(e) => {
                assert!(e.to_string().contains($error),
                        "bad error: {}", e);
            }
        }
    })
}

macro_rules! map( ($($k:ident: $v:expr),*) => ({
    let mut _m = BTreeMap::new();
    $(_m.insert(stringify!($k).to_string(), $v);)*
    _m
}) );

#[test]
fn smoke() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: isize }

    equivalent!(
        Foo { a: 2 },
        Table(map! { a: Integer(2) }),
    );
}

#[test]
fn smoke_hyphen() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo {
        a_b: isize,
    }

    equivalent! {
        Foo { a_b: 2 },
        Table(map! { a_b: Integer(2) }),
    }

    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo2 {
        #[serde(rename = "a-b")]
        a_b: isize,
    }

    let mut m = BTreeMap::new();
    m.insert("a-b".to_string(), Integer(2));
    equivalent! {
        Foo2 { a_b: 2 },
        Table(m),
    }
}

#[test]
fn nested() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: isize, b: Bar }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar { a: String }

    equivalent! {
        Foo { a: 2, b: Bar { a: "test".to_string() } },
        Table(map! {
            a: Integer(2),
            b: Table(map! {
                a: Value::String("test".to_string())
            })
        }),
    }
}

#[test]
fn application_decode_error() {
    #[derive(PartialEq, Debug)]
    struct Range10(usize);
    impl<'de> Deserialize<'de> for Range10 {
         fn deserialize<D: Deserializer<'de>>(d: D) -> Result<Range10, D::Error> {
             let x: usize = try!(Deserialize::deserialize(d));
             if x > 10 {
                 Err(serde::de::Error::custom("more than 10"))
             } else {
                 Ok(Range10(x))
             }
         }
    }
    let d_good = Integer(5);
    let d_bad1 = Value::String("not an isize".to_string());
    let d_bad2 = Integer(11);

    assert_eq!(Range10(5), d_good.try_into().unwrap());

    let err1: Result<Range10, _> = d_bad1.try_into();
    assert!(err1.is_err());
    let err2: Result<Range10, _> = d_bad2.try_into();
    assert!(err2.is_err());
}

#[test]
fn array() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Vec<isize> }

    equivalent! {
        Foo { a: vec![1, 2, 3, 4] },
        Table(map! {
            a: Array(vec![
                Integer(1),
                Integer(2),
                Integer(3),
                Integer(4)
            ])
        }),
    };
}

#[test]
fn inner_structs_with_options() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo {
        a: Option<Box<Foo>>,
        b: Bar,
    }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar {
        a: String,
        b: f64,
    }

    equivalent! {
        Foo {
            a: Some(Box::new(Foo {
                a: None,
                b: Bar { a: "foo".to_string(), b: 4.5 },
            })),
            b: Bar { a: "bar".to_string(), b: 1.0 },
        },
        Table(map! {
            a: Table(map! {
                b: Table(map! {
                    a: Value::String("foo".to_string()),
                    b: Float(4.5)
                })
            }),
            b: Table(map! {
                a: Value::String("bar".to_string()),
                b: Float(1.0)
            })
        }),
    }
}

#[test]
fn hashmap() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo {
        set: HashSet<char>,
        map: BTreeMap<String, isize>,
    }

    equivalent! {
        Foo {
            map: {
                let mut m = BTreeMap::new();
                m.insert("foo".to_string(), 10);
                m.insert("bar".to_string(), 4);
                m
            },
            set: {
                let mut s = HashSet::new();
                s.insert('a');
                s
            },
        },
        Table(map! {
            map: Table(map! {
                foo: Integer(10),
                bar: Integer(4)
            }),
            set: Array(vec![Value::String("a".to_string())])
        }),
    }
}

#[test]
fn table_array() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Vec<Bar>, }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar { a: isize }

    equivalent! {
        Foo { a: vec![Bar { a: 1 }, Bar { a: 2 }] },
        Table(map! {
            a: Array(vec![
                Table(map!{ a: Integer(1) }),
                Table(map!{ a: Integer(2) }),
            ])
        }),
    }
}

#[test]
fn type_errors() {
    #[derive(Deserialize)]
    #[allow(dead_code)]
    struct Foo { bar: isize }

    error! {
        Foo,
        Table(map! {
            bar: Value::String("a".to_string())
        }),
        "invalid type: string \"a\", expected isize for key `bar`"
    }

    #[derive(Deserialize)]
    #[allow(dead_code)]
    struct Bar { foo: Foo }

    error! {
        Bar,
        Table(map! {
            foo: Table(map! {
                bar: Value::String("a".to_string())
            })
        }),
        "invalid type: string \"a\", expected isize for key `foo.bar`"
    }
}

#[test]
fn missing_errors() {
    #[derive(Serialize, Deserialize, PartialEq, Debug)]
    struct Foo { bar: isize }

    error! {
        Foo,
        Table(map! { }),
        "missing field `bar`"
    }
}

#[test]
fn parse_enum() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: E }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    #[serde(untagged)]
    enum E {
        Bar(isize),
        Baz(String),
        Last(Foo2),
    }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo2 {
        test: String,
    }

    equivalent! {
        Foo { a: E::Bar(10) },
        Table(map! { a: Integer(10) }),
    }

    equivalent! {
        Foo { a: E::Baz("foo".to_string()) },
        Table(map! { a: Value::String("foo".to_string()) }),
    }

    equivalent! {
        Foo { a: E::Last(Foo2 { test: "test".to_string() }) },
        Table(map! { a: Table(map! { test: Value::String("test".to_string()) }) }),
    }
}

#[test]
fn parse_enum_string() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Sort }

    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    #[serde(rename_all = "lowercase")]
    enum Sort {
        Asc,
        Desc,
    }

    equivalent! {
        Foo { a: Sort::Desc },
        Table(map! { a: Value::String("desc".to_string()) }),
    }

}

// #[test]
// fn unused_fields() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: isize }
//
//     let v = Foo { a: 2 };
//     let mut d = Decoder::new(Table(map! {
//         a, Integer(2),
//         b, Integer(5)
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, Some(Table(map! {
//         b, Integer(5)
//     })));
// }
//
// #[test]
// fn unused_fields2() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Bar }
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Bar { a: isize }
//
//     let v = Foo { a: Bar { a: 2 } };
//     let mut d = Decoder::new(Table(map! {
//         a, Table(map! {
//             a, Integer(2),
//             b, Integer(5)
//         })
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, Some(Table(map! {
//         a, Table(map! {
//             b, Integer(5)
//         })
//     })));
// }
//
// #[test]
// fn unused_fields3() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Bar }
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Bar { a: isize }
//
//     let v = Foo { a: Bar { a: 2 } };
//     let mut d = Decoder::new(Table(map! {
//         a, Table(map! {
//             a, Integer(2)
//         })
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields4() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: BTreeMap<String, String> }
//
//     let v = Foo { a: map! { a, "foo".to_string() } };
//     let mut d = Decoder::new(Table(map! {
//         a, Table(map! {
//             a, Value::String("foo".to_string())
//         })
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields5() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Vec<String> }
//
//     let v = Foo { a: vec!["a".to_string()] };
//     let mut d = Decoder::new(Table(map! {
//         a, Array(vec![Value::String("a".to_string())])
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields6() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Option<Vec<String>> }
//
//     let v = Foo { a: Some(vec![]) };
//     let mut d = Decoder::new(Table(map! {
//         a, Array(vec![])
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, None);
// }
//
// #[test]
// fn unused_fields7() {
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Foo { a: Vec<Bar> }
//     #[derive(Serialize, Deserialize, PartialEq, Debug)]
//     struct Bar { a: isize }
//
//     let v = Foo { a: vec![Bar { a: 1 }] };
//     let mut d = Decoder::new(Table(map! {
//         a, Array(vec![Table(map! {
//             a, Integer(1),
//             b, Integer(2)
//         })])
//     }));
//     assert_eq!(v, t!(Deserialize::deserialize(&mut d)));
//
//     assert_eq!(d.toml, Some(Table(map! {
//         a, Array(vec![Table(map! {
//             b, Integer(2)
//         })])
//     })));
// }

#[test]
fn empty_arrays() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Vec<Bar> }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar;

    equivalent! {
        Foo { a: vec![] },
        Table(map! {a: Array(Vec::new())}),
    }
}

#[test]
fn empty_arrays2() {
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Foo { a: Option<Vec<Bar>> }
    #[derive(Serialize, Deserialize, PartialEq, Debug, Clone)]
    struct Bar;

    equivalent! {
        Foo { a: None },
        Table(map! {}),
    }

    equivalent!{
        Foo { a: Some(vec![]) },
        Table(map! { a: Array(vec![]) }),
    }
}

#[test]
fn extra_keys() {
    #[derive(Serialize, Deserialize)]
    struct Foo { a: isize }

    let toml = Table(map! { a: Integer(2), b: Integer(2) });
    assert!(toml.clone().try_into::<Foo>().is_ok());
    assert!(toml::from_str::<Foo>(&toml.to_string()).is_ok());
}
<|endoftext|>


<commit_before>extern crate xmltree;
extern crate tempfile;

use xmltree::Element;
use std::fs::File;
use tempfile::TempFile;

#[test]
fn test_01() {
    let e: Element = Element::parse(File::open("tests/data/01.xml").unwrap()).unwrap();
    println!("E:=======\n{:#?}", e);
    assert_eq!(e.name, "project");
    let e2: &Element = e.get_child("libraries").expect("Missing libraries child element");
    assert_eq!(e2.name, "libraries");

    assert!(e.get_child("doesnotexist").is_none());

    let mut tmp = TempFile::shared(2).unwrap();
    let t1 = tmp.remove(0);
    let t2 = tmp.remove(0);
    e.write(t1);

    let e2 = Element::parse(t2).unwrap();
    println!("E2:======\n{:#?}", e2);

    assert_eq!(e, e2);
}

#[test]
fn test_02() {
    let e: Element = Element::parse(File::open("tests/data/02.xml").unwrap()).unwrap();
    println!("{:#?}", e);
}

#[test]
fn test_03() {
    let e: Element = Element::parse(File::open("tests/data/03.xml").unwrap()).unwrap();
    println!("{:#?}", e);
}

#[test]
fn test_04() {
    let e: Element = Element::parse(File::open("tests/data/04.xml").unwrap()).unwrap();
    println!("{:#?}", e);
}

#[test]
fn test_rw() {

    let e: Element = Element::parse(File::open("tests/data/rw.xml").unwrap()).unwrap();

    let mut tmp = TempFile::shared(2).unwrap();
    let t1 = tmp.remove(0);
    let t2 = tmp.remove(0);
    e.write(t1);

    let e2 = Element::parse(t2).unwrap();

    assert_eq!(e, e2);
}

#[test]
fn test_mut() {

    let mut e: Element = Element::parse(File::open("tests/data/rw.xml").unwrap()).unwrap();
    {
        let name = e.get_mut_child("name").unwrap();
        name.attributes.insert("suffix".to_owned(), "mr".to_owned());
    }
}

<commit_msg>A few tests of the new error handling code<commit_after>extern crate xmltree;
extern crate tempfile;

use xmltree::Element;
use xmltree::ParseError;
use std::fs::File;
use tempfile::TempFile;

#[test]
fn test_01() {
    let e: Element = Element::parse(File::open("tests/data/01.xml").unwrap()).unwrap();
    println!("E:=======\n{:#?}", e);
    assert_eq!(e.name, "project");
    let e2: &Element = e.get_child("libraries").expect("Missing libraries child element");
    assert_eq!(e2.name, "libraries");

    assert!(e.get_child("doesnotexist").is_none());

    let mut tmp = TempFile::shared(2).unwrap();
    let t1 = tmp.remove(0);
    let t2 = tmp.remove(0);
    e.write(t1);

    let e2 = Element::parse(t2).unwrap();
    println!("E2:======\n{:#?}", e2);

    assert_eq!(e, e2);
}

#[test]
fn test_02() {
    let e: Element = Element::parse(File::open("tests/data/02.xml").unwrap()).unwrap();
    println!("{:#?}", e);
}

#[test]
fn test_03() {
    let e: Element = Element::parse(File::open("tests/data/03.xml").unwrap()).unwrap();
    println!("{:#?}", e);
}

#[test]
fn test_04() {
    let e: Element = Element::parse(File::open("tests/data/04.xml").unwrap()).unwrap();
    println!("{:#?}", e);
}

#[test]
fn test_rw() {

    let e: Element = Element::parse(File::open("tests/data/rw.xml").unwrap()).unwrap();

    let mut tmp = TempFile::shared(2).unwrap();
    let t1 = tmp.remove(0);
    let t2 = tmp.remove(0);
    e.write(t1);

    let e2 = Element::parse(t2).unwrap();

    assert_eq!(e, e2);
}

#[test]
fn test_mut() {

    let mut e: Element = Element::parse(File::open("tests/data/rw.xml").unwrap()).unwrap();
    {
        let name = e.get_mut_child("name").unwrap();
        name.attributes.insert("suffix".to_owned(), "mr".to_owned());
    }
}

#[test]
fn test_mal_01() {
    // some tests for error handling

    let data = r##"
        <?xml version="1.0" encoding="utf-8" standalone="yes"?>
        <names>
            <name first="bob" last="jones />
            <name first="elizabeth" last="smith" />
        </names>
    "##;

    let names_element = Element::parse(data.as_bytes());
    if let Err(ParseError::MalformedXml(..)) = names_element {
        // OK
    } else {
        panic!("unexpected parse result");
    }
    println!("{:?}", names_element);

}

#[test]
fn test_mal_02() {
    // some tests for error handling

    let data = r##"
            this is not even close
            to XML
    "##;

    let names_element = Element::parse(data.as_bytes());
    if let Err(ParseError::MalformedXml(..)) = names_element {
        // OK
    } else {
        panic!("unexpected parse result");
    }
    println!("{:?}", names_element);

}

#[test]
fn test_mal_03() {
    // some tests for error handling

    let data = r##"
        <?xml version="1.0" encoding="utf-8" standalone="yes"?>
        <names>
            <name first="bob" last="jones"></badtag>
            <name first="elizabeth" last="smith" />
        </names>
    "##;

    let names_element = Element::parse(data.as_bytes());
    if let Err(ParseError::MalformedXml(..)) = names_element {
        // OK
    } else {
        panic!("unexpected parse result");
    }
    println!("{:?}", names_element);

}
<|endoftext|>