<commit_before>extern crate diff;
extern crate lalrpop_util;

use std::cell::RefCell;

use lalrpop_util::{ErrorRecovery, ParseError};

use util::tok::Tok;

/// demonstration from the Greene text; one of the simplest grammars
/// that still ensures we get parse tree correct
mod sub;

/// test something other than test-all
mod sub_ascent;
mod sub_table;

/// more interesting demonstration of parsing full expressions
mod expr;

/// more interesting demonstration of parsing full expressions, using LALR not LR
mod expr_lalr;

/// more interesting demonstration of parsing full expressions, using intern tok
mod expr_intern_tok;

/// test that passes in lifetime/type/formal parameters and threads
/// them through, building an AST from the result
mod expr_arena;

/// definitions of the AST
mod expr_arena_ast;

/// expr defined with a generic type `F`
mod expr_generic;

mod generics_issue_104;
mod generics_issue_104_lib;

/// test of inlining
mod inline;

/// test that exercises internal token generation, as well as locations and spans
mod intern_tok;

/// test that exercises using a lifetime parameter in the token type
mod lifetime_tok;

/// library for lifetime_tok test
mod lifetime_tok_lib;

/// test that exercises locations and spans
mod loc;

/// regression test for location issue #90
mod loc_issue_90;
mod loc_issue_90_lib;

/// test that uses `super` in paths in various places
mod use_super;

/// test that exercises locations and spans
mod error;

/// Test error recovery
mod error_recovery;
mod error_recovery_pull_182;

/// test for inlining expansion issue #55
mod issue_55;

/// test for unit action code
mod unit;

/// test for match section
mod match_section;

// Check that error recovery (which requires cloneable tokens) is not created if it is not used
#[allow(unused)]
mod no_clone_tok;

mod util;

/// This constant is here so that some of the generator parsers can
/// refer to it in order to test `super::` handling in action code.
const ZERO: i32 = 0;

#[test]
fn expr_test1() {
    util::test(|v| expr::parse_Expr(1, v), "22 - 3", 22 - 3);
}

#[test]
fn expr_test2() {
    util::test(|v| expr::parse_Expr(1, v), "22 - (3 + 5)", 22 - (3 + 5));
}

#[test]
fn expr_test3() {
    util::test(|v| expr::parse_Expr(1, v), "22 - (3 - 5) - 13", 22 - (3 - 5) - 13);
}

#[test]
fn expr_test4() {
    util::test(|v| expr::parse_Expr(1, v), "22 * 3 - 6", 22 * 3 - 6);
}

#[test]
fn expr_test5() {
    util::test(|v| expr::parse_Expr(11, v), "22 * 3 - 6", 22*11 * 3*11 - 6*11);
}

#[test]
fn expr_intern_tok_test1() {
    assert_eq!(expr_intern_tok::parse_Expr(1, "22 - 3").unwrap(), 22 - 3);
}

#[test]
fn expr_intern_tok_test2() {
    assert_eq!(expr_intern_tok::parse_Expr(1, "22 - (3 - 5) - 13").unwrap(), 22 - (3 - 5) - 13);
}

#[test]
fn expr_intern_tok_test_err() {
    match expr_intern_tok::parse_Expr(1, "22 - (3 - 5) - X") {
        //                                0123456789012345
        Err(ParseError::InvalidToken { location }) => {
            assert_eq!(location, 15);
        }
        r => {
            panic!("invalid result {:?}", r);
        }
    }
}

#[test]
fn expr_lifetime_tok1() {
    // the problem here was that we were improperly pruning the 'input from the
    let tokens = lifetime_tok_lib::lt_tokenize("x");
    let tree = lifetime_tok::parse_Expr(tokens).unwrap();
    assert_eq!(tree, vec!["x"]);
}

#[test]
fn expr_lalr_test1() {
    util::test(|v| expr_lalr::parse_Expr(1, v), "22 - 3", 22 - 3);
}

#[test]
fn expr_lalr_test2() {
    util::test(|v| expr_lalr::parse_Expr(1, v), "22 - (3 + 5)", 22 - (3 + 5));
}

#[test]
fn expr_lalr_test3() {
    util::test(|v| expr_lalr::parse_Expr(1, v), "22 - (3 - 5) - 13", 22 - (3 - 5) - 13);
}

#[test]
fn expr_lalr_test4() {
    util::test(|v| expr_lalr::parse_Expr(1, v), "22 * 3 - 6", 22 * 3 - 6);
}

#[test]
fn expr_lalr_test5() {
    util::test(|v| expr_lalr::parse_Expr(11, v), "22 * 3 - 6", 22*11 * 3*11 - 6*11);
}

#[test]
fn inline_test1() {
    assert_eq!(inline::parse_E("& L L").unwrap(), "& L L");
}

#[test]
fn sub_test1() {
    util::test(sub::parse_S, "22 - 3", 22 - 3);
}

#[test]
fn sub_test2() {
    util::test(sub::parse_S, "22 - (3 - 5)", 22 - (3 - 5));
}

#[test]
fn sub_test3() {
    util::test(sub::parse_S, "22 - (3 - 5) - 13", 22 - (3 - 5) - 13);
}

#[test]
fn sub_ascent_test1() {
    util::test(sub_ascent::parse_S, "22 - 3", 22 - 3);
}

#[test]
fn sub_table_test1() {
    util::test(sub_table::parse_S, "22 - 3", 22 - 3);
}

#[test]
fn expr_arena_test1() {
    use expr_arena_ast::*;
    let arena = Arena::new();
    let expected =
        arena.alloc(Node::Binary(Op::Sub,
                                 arena.alloc(Node::Binary(Op::Mul,
                                                          arena.alloc(Node::Value(22)),
                                                          arena.alloc(Node::Value(3)))),
                                 arena.alloc(Node::Value(6))));
    util::test_loc(|v| expr_arena::parse_Expr(&arena, v), "22 * 3 - 6", expected);
}

#[test]
fn expr_arena_test2() {
    use expr_arena_ast::*;
    let arena = Arena::new();
    let expected =
        arena.alloc(Node::Reduce(Op::Mul,
                                 vec![arena.alloc(Node::Value(22)),
                                      arena.alloc(Node::Value(3)),
                                      arena.alloc(Node::Value(6))]));;
    util::test_loc(|v| expr_arena::parse_Expr(&arena, v), "*(22, 3, 6)", expected);
    util::test_loc(|v| expr_arena::parse_Expr(&arena, v), "*(22, 3, 6,)", expected);
}

#[test]
fn expr_arena_test3() {
    use expr_arena_ast::*;
    let arena = Arena::new();
    let expected =
        arena.alloc(
            Node::Binary(Op::Mul,
                         arena.alloc(Node::Value(22)),
                         arena.alloc(Node::Paren(
                             arena.alloc(
                                 Node::Binary(Op::Sub,
                                              arena.alloc(Node::Value(3)),
                                              arena.alloc(Node::Value(6))))))));
    util::test_loc(|v| expr_arena::parse_Expr(&arena, v), "22 * (3 - 6)", expected);
}

#[test]
fn expr_generic_test1() {
    let expected: i32 = 22 * 3 - 6;
    let actual = expr_generic::parse_Expr::<i32>("22 * 3 - 6").unwrap();
    assert_eq!(expected, actual);
}

#[test]
fn intern_tok_test1() {
    let expected = vec![(0, 0), // spans of `+` characters, measured in bytes
                        (2, 3),
                        (4, 5),
                        (8, 9)];
    let actual = intern_tok::parse_Items("--+-+---+").unwrap();
    //                                    012345678
    assert_eq!(actual, expected);
}

#[test]
fn loc_test1() {
    let expected = vec![(0, 0), // note that tok.rs generates odd spans, measured in 2*chars
                        (4, 5),
                        (8, 9),
                        (16, 17)];
    util::test_loc(|v| loc::parse_Items(v), "--+-+---+", expected);
    //                                       000001111
    //                                       024680246
}

#[test]
fn loc_test2() {
    util::test_loc(|v| loc::parse_Items(v), "+", vec![(0, 0),
                                                      (0, 1)]);
}

#[test]
fn loc_empty() {
    // test what happens when `@L` and `@R` are invoked on an empty input
    util::test_loc(|v| loc::parse_Items(v), "", vec![(0, 0)]);
}

#[test]
fn use_super_test1() {
    util::test(|v| use_super::parse_S(v), "()", 0);
}

#[test]
fn error_test1() {
    match util::test_err_gen(error::parse_Items, "---+") {
        Err(ParseError::User { error: '+' }) => { /* OK! */ }
        r => {
            panic!("unexpected response from parser: {:?}", r);
        }
    }
}

#[test]
fn error_recovery_eof() {
    let errors = RefCell::new(vec![]);
    util::test(|v| error_recovery::parse_Item(&errors, v), "--", '!'.to_string());

    assert_eq!(errors.borrow().len(), 1);
    assert_eq!(errors.borrow()[0], ErrorRecovery {
        error: ParseError::UnrecognizedToken {
            token: None,
            expected: vec!["\"-\"".to_string()],
        },
        dropped_tokens: vec![],
    });
}

#[test]
fn error_recovery_eof_without_recovery() {
    let errors = RefCell::new(vec![]);
    let tokens = util::tok::tokenize("-").into_iter().map(|t| t.1);
    let result = error_recovery::parse_Item(&errors, tokens);
    assert_eq!(result, Err(ParseError::UnrecognizedToken {
        token: None,
        expected: vec!["\"-\"".to_string()],
    }));
}

#[test]
fn error_recovery_extra_token() {
    let errors = RefCell::new(vec![]);
    util::test(|v| error_recovery::parse_Item(&errors, v), "(++)", "()".to_string());

    assert_eq!(errors.borrow().len(), 1);
    assert_eq!(errors.borrow()[0], ErrorRecovery {
        error: ParseError::UnrecognizedToken {
            token: Some(((), Tok::Plus,())),
            expected: vec!["\")\"".to_string()],
        },
        dropped_tokens: vec![((), Tok::Plus, ())],
    });
}

#[test]
fn error_recovery_dont_drop_unrecognized_token() {
    let errors = RefCell::new(vec![]);
    util::test(|v| error_recovery::parse_Item(&errors, v), "(--)", "(!)".to_string());

    assert_eq!(errors.borrow().len(), 1);
    assert_eq!(errors.borrow()[0], ErrorRecovery {
        error: ParseError::UnrecognizedToken {
            token: Some(((), Tok::RParen,())),
            expected: vec!["\"-\"".to_string()],
        },
        dropped_tokens: vec![],
    });
}

#[test]
fn error_recovery_multiple_extra_tokens() {
    let errors = RefCell::new(vec![]);
    util::test(|v| error_recovery::parse_Item(&errors, v), "(+++)", "()".to_string());

    assert_eq!(errors.borrow().len(), 1);
    assert_eq!(errors.borrow()[0], ErrorRecovery {
        error: ParseError::UnrecognizedToken {
            token: Some(((), Tok::Plus,())),
            expected: vec!["\")\"".to_string()],
        },
        dropped_tokens: vec![((), Tok::Plus, ()), ((), Tok::Plus, ())],
    });
}

#[test]
fn error_recovery_dont_panic_on_reduce_normal() {
    let mut errors = vec![];
    util::test(|v| error_recovery_pull_182::parse_Item(&mut errors, v), "1+/", ());

    assert_eq!(errors.len(), 1);
}

#[test]
fn error_recovery_dont_panic_on_reduce_at_eof() {
    let mut errors = vec![];
    util::test(|v| error_recovery_pull_182::parse_Item(&mut errors, v), "1+", ());

    assert_eq!(errors.len(), 1);
}

#[test]
fn issue_55_test1() {
    // Issue 55 caused us to either accept NO assoc types or assoc
    // types both before and after, so check that we can parse with
    // assoc types on either side.

    let (a, b, c) = issue_55::parse_E("{ type X; type Y; enum Z { } }").unwrap();
    assert_eq!(a, vec!["X", "Y"]);
    assert_eq!(b, "Z");
    assert!(c.is_empty());

    let (a, b, c) = issue_55::parse_E("{ enum Z { } type X; type Y; }").unwrap();
    assert!(a.is_empty());
    assert_eq!(b, "Z");
    assert_eq!(c, vec!["X", "Y"]);
}

#[test]
fn unit_test1() {
    assert!(unit::parse_Expr("3 + 4 * 5").is_ok());
    assert!(unit::parse_Expr("3 + +").is_err());
}

#[test]
fn generics_issue_104_test1() {
    // The real thing `generics_issue_104` is testing is that the code
    // *compiles*, even though the type parameter `T` does not appear
    // in any of the arguments.
    assert!(generics_issue_104::parse_Schema::<()>("grammar { foo }").is_ok());
}

#[test]
fn match_section() {
    assert!(match_section::parse_Query("SELECT foo").is_ok());
    assert!(match_section::parse_Query("select foo").is_ok());
    assert!(match_section::parse_Query("INSERT foo").is_ok());
    assert!(match_section::parse_Query("UPDATE foo").is_ok());
    assert!(match_section::parse_Query("UPDATE update").is_err());
}
<commit_msg>break apart the `unit` tests into two<commit_after>extern crate diff;
extern crate lalrpop_util;

use std::cell::RefCell;

use lalrpop_util::{ErrorRecovery, ParseError};

use util::tok::Tok;

/// demonstration from the Greene text; one of the simplest grammars
/// that still ensures we get parse tree correct
mod sub;

/// test something other than test-all
mod sub_ascent;
mod sub_table;

/// more interesting demonstration of parsing full expressions
mod expr;

/// more interesting demonstration of parsing full expressions, using LALR not LR
mod expr_lalr;

/// more interesting demonstration of parsing full expressions, using intern tok
mod expr_intern_tok;

/// test that passes in lifetime/type/formal parameters and threads
/// them through, building an AST from the result
mod expr_arena;

/// definitions of the AST
mod expr_arena_ast;

/// expr defined with a generic type `F`
mod expr_generic;

mod generics_issue_104;
mod generics_issue_104_lib;

/// test of inlining
mod inline;

/// test that exercises internal token generation, as well as locations and spans
mod intern_tok;

/// test that exercises using a lifetime parameter in the token type
mod lifetime_tok;

/// library for lifetime_tok test
mod lifetime_tok_lib;

/// test that exercises locations and spans
mod loc;

/// regression test for location issue #90
mod loc_issue_90;
mod loc_issue_90_lib;

/// test that uses `super` in paths in various places
mod use_super;

/// test that exercises locations and spans
mod error;

/// Test error recovery
mod error_recovery;
mod error_recovery_pull_182;

/// test for inlining expansion issue #55
mod issue_55;

/// test for unit action code
mod unit;

/// test for match section
mod match_section;

// Check that error recovery (which requires cloneable tokens) is not created if it is not used
#[allow(unused)]
mod no_clone_tok;

mod util;

/// This constant is here so that some of the generator parsers can
/// refer to it in order to test `super::` handling in action code.
const ZERO: i32 = 0;

#[test]
fn expr_test1() {
    util::test(|v| expr::parse_Expr(1, v), "22 - 3", 22 - 3);
}

#[test]
fn expr_test2() {
    util::test(|v| expr::parse_Expr(1, v), "22 - (3 + 5)", 22 - (3 + 5));
}

#[test]
fn expr_test3() {
    util::test(|v| expr::parse_Expr(1, v), "22 - (3 - 5) - 13", 22 - (3 - 5) - 13);
}

#[test]
fn expr_test4() {
    util::test(|v| expr::parse_Expr(1, v), "22 * 3 - 6", 22 * 3 - 6);
}

#[test]
fn expr_test5() {
    util::test(|v| expr::parse_Expr(11, v), "22 * 3 - 6", 22*11 * 3*11 - 6*11);
}

#[test]
fn expr_intern_tok_test1() {
    assert_eq!(expr_intern_tok::parse_Expr(1, "22 - 3").unwrap(), 22 - 3);
}

#[test]
fn expr_intern_tok_test2() {
    assert_eq!(expr_intern_tok::parse_Expr(1, "22 - (3 - 5) - 13").unwrap(), 22 - (3 - 5) - 13);
}

#[test]
fn expr_intern_tok_test_err() {
    match expr_intern_tok::parse_Expr(1, "22 - (3 - 5) - X") {
        //                                0123456789012345
        Err(ParseError::InvalidToken { location }) => {
            assert_eq!(location, 15);
        }
        r => {
            panic!("invalid result {:?}", r);
        }
    }
}

#[test]
fn expr_lifetime_tok1() {
    // the problem here was that we were improperly pruning the 'input from the
    let tokens = lifetime_tok_lib::lt_tokenize("x");
    let tree = lifetime_tok::parse_Expr(tokens).unwrap();
    assert_eq!(tree, vec!["x"]);
}

#[test]
fn expr_lalr_test1() {
    util::test(|v| expr_lalr::parse_Expr(1, v), "22 - 3", 22 - 3);
}

#[test]
fn expr_lalr_test2() {
    util::test(|v| expr_lalr::parse_Expr(1, v), "22 - (3 + 5)", 22 - (3 + 5));
}

#[test]
fn expr_lalr_test3() {
    util::test(|v| expr_lalr::parse_Expr(1, v), "22 - (3 - 5) - 13", 22 - (3 - 5) - 13);
}

#[test]
fn expr_lalr_test4() {
    util::test(|v| expr_lalr::parse_Expr(1, v), "22 * 3 - 6", 22 * 3 - 6);
}

#[test]
fn expr_lalr_test5() {
    util::test(|v| expr_lalr::parse_Expr(11, v), "22 * 3 - 6", 22*11 * 3*11 - 6*11);
}

#[test]
fn inline_test1() {
    assert_eq!(inline::parse_E("& L L").unwrap(), "& L L");
}

#[test]
fn sub_test1() {
    util::test(sub::parse_S, "22 - 3", 22 - 3);
}

#[test]
fn sub_test2() {
    util::test(sub::parse_S, "22 - (3 - 5)", 22 - (3 - 5));
}

#[test]
fn sub_test3() {
    util::test(sub::parse_S, "22 - (3 - 5) - 13", 22 - (3 - 5) - 13);
}

#[test]
fn sub_ascent_test1() {
    util::test(sub_ascent::parse_S, "22 - 3", 22 - 3);
}

#[test]
fn sub_table_test1() {
    util::test(sub_table::parse_S, "22 - 3", 22 - 3);
}

#[test]
fn expr_arena_test1() {
    use expr_arena_ast::*;
    let arena = Arena::new();
    let expected =
        arena.alloc(Node::Binary(Op::Sub,
                                 arena.alloc(Node::Binary(Op::Mul,
                                                          arena.alloc(Node::Value(22)),
                                                          arena.alloc(Node::Value(3)))),
                                 arena.alloc(Node::Value(6))));
    util::test_loc(|v| expr_arena::parse_Expr(&arena, v), "22 * 3 - 6", expected);
}

#[test]
fn expr_arena_test2() {
    use expr_arena_ast::*;
    let arena = Arena::new();
    let expected =
        arena.alloc(Node::Reduce(Op::Mul,
                                 vec![arena.alloc(Node::Value(22)),
                                      arena.alloc(Node::Value(3)),
                                      arena.alloc(Node::Value(6))]));;
    util::test_loc(|v| expr_arena::parse_Expr(&arena, v), "*(22, 3, 6)", expected);
    util::test_loc(|v| expr_arena::parse_Expr(&arena, v), "*(22, 3, 6,)", expected);
}

#[test]
fn expr_arena_test3() {
    use expr_arena_ast::*;
    let arena = Arena::new();
    let expected =
        arena.alloc(
            Node::Binary(Op::Mul,
                         arena.alloc(Node::Value(22)),
                         arena.alloc(Node::Paren(
                             arena.alloc(
                                 Node::Binary(Op::Sub,
                                              arena.alloc(Node::Value(3)),
                                              arena.alloc(Node::Value(6))))))));
    util::test_loc(|v| expr_arena::parse_Expr(&arena, v), "22 * (3 - 6)", expected);
}

#[test]
fn expr_generic_test1() {
    let expected: i32 = 22 * 3 - 6;
    let actual = expr_generic::parse_Expr::<i32>("22 * 3 - 6").unwrap();
    assert_eq!(expected, actual);
}

#[test]
fn intern_tok_test1() {
    let expected = vec![(0, 0), // spans of `+` characters, measured in bytes
                        (2, 3),
                        (4, 5),
                        (8, 9)];
    let actual = intern_tok::parse_Items("--+-+---+").unwrap();
    //                                    012345678
    assert_eq!(actual, expected);
}

#[test]
fn loc_test1() {
    let expected = vec![(0, 0), // note that tok.rs generates odd spans, measured in 2*chars
                        (4, 5),
                        (8, 9),
                        (16, 17)];
    util::test_loc(|v| loc::parse_Items(v), "--+-+---+", expected);
    //                                       000001111
    //                                       024680246
}

#[test]
fn loc_test2() {
    util::test_loc(|v| loc::parse_Items(v), "+", vec![(0, 0),
                                                      (0, 1)]);
}

#[test]
fn loc_empty() {
    // test what happens when `@L` and `@R` are invoked on an empty input
    util::test_loc(|v| loc::parse_Items(v), "", vec![(0, 0)]);
}

#[test]
fn use_super_test1() {
    util::test(|v| use_super::parse_S(v), "()", 0);
}

#[test]
fn error_test1() {
    match util::test_err_gen(error::parse_Items, "---+") {
        Err(ParseError::User { error: '+' }) => { /* OK! */ }
        r => {
            panic!("unexpected response from parser: {:?}", r);
        }
    }
}

#[test]
fn error_recovery_eof() {
    let errors = RefCell::new(vec![]);
    util::test(|v| error_recovery::parse_Item(&errors, v), "--", '!'.to_string());

    assert_eq!(errors.borrow().len(), 1);
    assert_eq!(errors.borrow()[0], ErrorRecovery {
        error: ParseError::UnrecognizedToken {
            token: None,
            expected: vec!["\"-\"".to_string()],
        },
        dropped_tokens: vec![],
    });
}

#[test]
fn error_recovery_eof_without_recovery() {
    let errors = RefCell::new(vec![]);
    let tokens = util::tok::tokenize("-").into_iter().map(|t| t.1);
    let result = error_recovery::parse_Item(&errors, tokens);
    assert_eq!(result, Err(ParseError::UnrecognizedToken {
        token: None,
        expected: vec!["\"-\"".to_string()],
    }));
}

#[test]
fn error_recovery_extra_token() {
    let errors = RefCell::new(vec![]);
    util::test(|v| error_recovery::parse_Item(&errors, v), "(++)", "()".to_string());

    assert_eq!(errors.borrow().len(), 1);
    assert_eq!(errors.borrow()[0], ErrorRecovery {
        error: ParseError::UnrecognizedToken {
            token: Some(((), Tok::Plus,())),
            expected: vec!["\")\"".to_string()],
        },
        dropped_tokens: vec![((), Tok::Plus, ())],
    });
}

#[test]
fn error_recovery_dont_drop_unrecognized_token() {
    let errors = RefCell::new(vec![]);
    util::test(|v| error_recovery::parse_Item(&errors, v), "(--)", "(!)".to_string());

    assert_eq!(errors.borrow().len(), 1);
    assert_eq!(errors.borrow()[0], ErrorRecovery {
        error: ParseError::UnrecognizedToken {
            token: Some(((), Tok::RParen,())),
            expected: vec!["\"-\"".to_string()],
        },
        dropped_tokens: vec![],
    });
}

#[test]
fn error_recovery_multiple_extra_tokens() {
    let errors = RefCell::new(vec![]);
    util::test(|v| error_recovery::parse_Item(&errors, v), "(+++)", "()".to_string());

    assert_eq!(errors.borrow().len(), 1);
    assert_eq!(errors.borrow()[0], ErrorRecovery {
        error: ParseError::UnrecognizedToken {
            token: Some(((), Tok::Plus,())),
            expected: vec!["\")\"".to_string()],
        },
        dropped_tokens: vec![((), Tok::Plus, ()), ((), Tok::Plus, ())],
    });
}

#[test]
fn error_recovery_dont_panic_on_reduce_normal() {
    let mut errors = vec![];
    util::test(|v| error_recovery_pull_182::parse_Item(&mut errors, v), "1+/", ());

    assert_eq!(errors.len(), 1);
}

#[test]
fn error_recovery_dont_panic_on_reduce_at_eof() {
    let mut errors = vec![];
    util::test(|v| error_recovery_pull_182::parse_Item(&mut errors, v), "1+", ());

    assert_eq!(errors.len(), 1);
}

#[test]
fn issue_55_test1() {
    // Issue 55 caused us to either accept NO assoc types or assoc
    // types both before and after, so check that we can parse with
    // assoc types on either side.

    let (a, b, c) = issue_55::parse_E("{ type X; type Y; enum Z { } }").unwrap();
    assert_eq!(a, vec!["X", "Y"]);
    assert_eq!(b, "Z");
    assert!(c.is_empty());

    let (a, b, c) = issue_55::parse_E("{ enum Z { } type X; type Y; }").unwrap();
    assert!(a.is_empty());
    assert_eq!(b, "Z");
    assert_eq!(c, vec!["X", "Y"]);
}

#[test]
fn unit_test1() {
    assert!(unit::parse_Expr("3 + 4 * 5").is_ok());
}

#[test]
fn unit_test2() {
    assert!(unit::parse_Expr("3 + +").is_err());
}

#[test]
fn generics_issue_104_test1() {
    // The real thing `generics_issue_104` is testing is that the code
    // *compiles*, even though the type parameter `T` does not appear
    // in any of the arguments.
    assert!(generics_issue_104::parse_Schema::<()>("grammar { foo }").is_ok());
}

#[test]
fn match_section() {
    assert!(match_section::parse_Query("SELECT foo").is_ok());
    assert!(match_section::parse_Query("select foo").is_ok());
    assert!(match_section::parse_Query("INSERT foo").is_ok());
    assert!(match_section::parse_Query("UPDATE foo").is_ok());
    assert!(match_section::parse_Query("UPDATE update").is_err());
}
<|endoftext|>


<commit_before>use std::collections::{BTreeMap, HashSet};
use std::hash::Hasher;
use std::iter;
use std::str::CharIndices;
use std::ops;

use itertools::{GroupBy, Itertools};

use util::*;
use super::*;
use Query::*;

/// A basic implementation of an `Index`, the inverted index is a data structure that maps
/// from words to postings.
#[derive(Debug)]
pub struct InvertedIndex {
    // Maps terms to their postings
    index: BTreeMap<String, PostingsMap>,
    // Maps doc ids to their docs
    docs: BTreeMap<String, Document>,
}

impl InvertedIndex {
    /// Constructs a new, empty InvertedIndex
    pub fn new() -> InvertedIndex {
        InvertedIndex { index: BTreeMap::new(), docs: BTreeMap::new() }
    }

    /// A basic implementation of index, splits the document's content into whitespace-separated
    /// words, and inserts each word-document pair into the map.
    pub fn index(&mut self, doc: Document) {
        let analyzed = analyze_doc(doc.content());
        let previous_version = self.docs.insert(doc.id.clone(), doc.clone());
        if let Some(previous_version) = previous_version {
            let previous_analyzed = analyze_doc(previous_version.content());
            for (ngram, _) in previous_analyzed {
                let is_empty = {
                    let docs_for_ngram = self.index.get_mut(&ngram).unwrap();
                    docs_for_ngram.remove(&doc.id);
                    docs_for_ngram.is_empty()
                };
                if is_empty {
                    self.index.remove(&ngram);
                }
            }
        }

        for (ngram, highlighted) in analyzed {
            let mut highlights = self.index
                                     .entry(ngram)
                                     .or_insert_with(BTreeMap::new)
                                     .entry(doc.id.clone())
                                     .or_insert_with(Vec::new);
            let coalesce_idx = highlights.binary_search(&highlighted).err().unwrap();
            highlights.coalesce(coalesce_idx, highlighted);
        }
    }

    /// Performs a search to the specification of the given query
    pub fn query(&self, query: &Query) -> Vec<SearchResult> {
        let postings = self.query_rec(query);
        self.compute_results(postings)
    }

    /// A helper method for performing a Match query
    pub fn search(&self, query: &str) -> Vec<SearchResult> {
        self.query(&Match(query))
    }

    fn postings(&self, query: &str) -> PostingsMap {
        let unique_terms: HashSet<_> = query.split_whitespace().map(str::to_lowercase).collect();
        unique_terms.into_iter().flat_map(|word| self.index.get(&word)).merge_postings()
    }

    fn query_rec(&self, query: &Query) -> PostingsMap {
        match *query {
            Match(query) => self.postings(query),
            And(queries) => {
                let postings: Vec<_> = queries.iter().map(|q| self.query_rec(q)).collect();
                postings.intersect_postings()
            }
            Or(queries) => queries.into_iter().map(|q| self.query_rec(q)).merge_postings(),
        }
    }

    fn compute_results(&self, postings: PostingsMap) -> Vec<SearchResult> {
        let mut results: Vec<_> = postings.into_iter()
                                          .map(|(doc_id, index_map)| {
                                              SearchResult::new(&self.docs[&doc_id],
                                                                index_map)
                                          })
                                          .collect();
        results.sort_by(|result1, result2| result2.score.partial_cmp(&result1.score).unwrap());
        results
    }
}

#[allow(unused_attributes)]
#[rustfmt_skip]
fn analyze_doc(doc: &str) ->
    iter::FlatMap<
        iter::Filter<
            GroupBy<bool, CharIndices, fn(&(usize, char)) -> bool>,
            fn(&(bool, Vec<(usize, char)>)) -> bool>,
        iter::Map<ops::Range<usize>, Ngrams>,
        fn((bool, Vec<(usize, char)>)) -> iter::Map<ops::Range<usize>, Ngrams>>
{
    doc.char_indices()
        .group_by(is_whitespace as fn(&(usize, char)) -> bool)
        .filter(not_whitespace as fn(&(bool, Vec<(usize, char)>)) -> bool)
        .flat_map(ngrams)
}

fn ngrams((_, chars): (bool, Vec<(usize, char)>)) -> iter::Map<ops::Range<usize>, Ngrams> {
    (1..chars.len() + 1).map(Ngrams::new(chars))
}

fn not_whitespace(&(is_whitespace, _): &(bool, Vec<(usize, char)>)) -> bool {
    !is_whitespace
}

fn is_whitespace(&(_, c): &(usize, char)) -> bool {
    c.is_whitespace()
}

struct Ngrams {
    chars: Vec<(usize, char)>,
}

impl Ngrams {
    fn new(chars: Vec<(usize, char)>) -> Ngrams {
        Ngrams { chars: chars }
    }
}

impl Fn<(usize,)> for Ngrams {
    extern "rust-call" fn call(&self, (to,): (usize,)) -> (String, (usize, usize)) {
        let word = self.chars[..to].iter().flat_map(|&(_, c)| c.to_lowercase()).collect();
        let start = self.chars[0].0;
        let (last_idx, last_char) = self.chars[to - 1];
        let finish = last_idx + last_char.len_utf8();
        (word, (start, finish))
    }
}

impl FnMut<(usize,)> for Ngrams {
    extern "rust-call" fn call_mut(&mut self, to: (usize,)) -> (String, (usize, usize)) {
        self.call(to)
    }
}

impl FnOnce<(usize,)> for Ngrams {
    type Output = (String, (usize, usize));
    extern "rust-call" fn call_once(self, to: (usize,)) -> (String, (usize, usize)) {
        self.call(to)
    }
}

#[cfg(test)]
mod test {
    use super::super::*;
    use Query::{And, Match, Or};
    use std::collections::BTreeMap;

    #[test]
    fn test_ngrams() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("1", "learn to program in rust today");
        let doc2 = Document::new("2", "what did you today do");
        index.index(doc1.clone());
        index.index(doc2.clone());
        let search_results = index.search("to");
        let expected: BTreeMap<_, _> = [(doc1.clone(), vec![(6, 8), (25, 27)]),
                                        (doc2, vec![(13, 15)])]
                                           .iter()
                                           .cloned()
                                           .collect();
        assert_eq!(search_results.len(), expected.len());
        for search_result in &search_results {
            assert_eq!(&search_result.highlights, &expected[search_result.doc])
        }
        assert_eq!("learn to program in rust today",
                   search_results.iter()
                                 .find(|search_result| search_result.doc == &doc1)
                                 .unwrap()
                                 .highlight("", ""));

    }

    #[test]
    fn test_highlight() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("2", "Won\u{2019}t this split the ecosystem? Will everyone use?");
        index.index(doc1.clone());
        let expected = "Won\u{2019}t this split the *e*cosystem? Will *e*veryone use?";
        let search_results = index.search("e");
        assert_eq!(1, search_results.len());
        assert_eq!(search_results[0].highlight("*", "*"), expected);
    }

    #[test]
    fn test_unicode() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "嗨, 您好");
        index.index(doc.clone());
        let to_search = "您";
        let search_results = index.search(to_search);
        let &SearchResult { ref doc, ref highlights, .. } = search_results.iter().next().unwrap();
        let (begin, end) = highlights[0];
        assert_eq!(&doc.content()[begin..end], to_search);
    }

    #[test]
    fn test_update_doc() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "abc åäö");
        index.index(doc);
        let doc = Document::new("0", "different");
        index.index(doc);
        let search_results = index.search("å");
        assert!(search_results.is_empty());
        assert_eq!(index.docs.len(), 1);
    }

    #[test]
    fn test_ranking() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let doc2 = Document::new("1", "beast");
        index.index(doc2);
        let search_results = index.search("be");
        assert_eq!(index.docs.len(), 2);
        // "beat" should be first, since it's a closer match
        assert_eq!(search_results[0].doc, &doc);
    }

    #[test]
    fn test_duplicate_term() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let search_results = index.search("be be");
        assert_eq!(search_results.len(), 1);
    }

    #[test]
    fn test_duplicate_term2() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let search_results = index.search("be b");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0].highlights, vec![(0, 2)]);
    }

    #[test]
    fn test_lowercase_search() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "BeAt");
        index.index(doc.clone());
        let search_results = index.search("bE");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0].highlights, vec![(0, 2)]);
    }

    #[test]
    fn test_lowercase_index() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "BeAt");
        index.index(doc.clone());
        let search_results = index.search("be");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0].highlights, vec![(0, 2)]);
    }

    #[test]
    fn test_and() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("1", "learn to program in rust today");
        let doc2 = Document::new("2", "what did you today do");
        let doc3 = Document::new("3", "what did you do yesterday");
        index.index(doc1.clone());
        index.index(doc2.clone());
        index.index(doc3.clone());
        let search_results = index.query(&And(&[Match("today"), Match("you")]));
        let expected: BTreeMap<_, _> = [(doc2, vec![(9, 12), (13, 18)])]
                                           .iter()
                                           .cloned()
                                           .collect();
        assert_eq!(search_results.len(), expected.len());
        for search_result in &search_results {
            assert_eq!(&search_result.highlights, &expected[search_result.doc])
        }
    }

    #[test]
    fn test_and_or() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("1", "learn to program in rust today");
        let doc2 = Document::new("2", "what did you today do");
        let doc3 = Document::new("3", "what did you do yesterday");
        index.index(doc1.clone());
        index.index(doc2.clone());
        index.index(doc3.clone());
        let search_results = index.query(&Or(&[Match("you"), And(&[Match("today"), Match("you")])]));
        let expected: BTreeMap<_, _> = [(doc2, vec![(9, 12), (13, 18)]), (doc3, vec![(9, 12)])]
                                           .iter()
                                           .cloned()
                                           .collect();
        assert_eq!(search_results.len(), expected.len());
        for search_result in &search_results {
            assert_eq!(&search_result.highlights, &expected[search_result.doc])
        }
    }
}
<commit_msg>Use search_coalesce helper<commit_after>use std::collections::{BTreeMap, HashSet};
use std::hash::Hasher;
use std::iter;
use std::str::CharIndices;
use std::ops;

use itertools::{GroupBy, Itertools};

use util::*;
use super::*;
use Query::*;

/// A basic implementation of an `Index`, the inverted index is a data structure that maps
/// from words to postings.
#[derive(Debug)]
pub struct InvertedIndex {
    // Maps terms to their postings
    index: BTreeMap<String, PostingsMap>,
    // Maps doc ids to their docs
    docs: BTreeMap<String, Document>,
}

impl InvertedIndex {
    /// Constructs a new, empty InvertedIndex
    pub fn new() -> InvertedIndex {
        InvertedIndex { index: BTreeMap::new(), docs: BTreeMap::new() }
    }

    /// A basic implementation of index, splits the document's content into whitespace-separated
    /// words, and inserts each word-document pair into the map.
    pub fn index(&mut self, doc: Document) {
        let analyzed = analyze_doc(doc.content());
        let previous_version = self.docs.insert(doc.id.clone(), doc.clone());
        if let Some(previous_version) = previous_version {
            let previous_analyzed = analyze_doc(previous_version.content());
            for (ngram, _) in previous_analyzed {
                let is_empty = {
                    let docs_for_ngram = self.index.get_mut(&ngram).unwrap();
                    docs_for_ngram.remove(&doc.id);
                    docs_for_ngram.is_empty()
                };
                if is_empty {
                    self.index.remove(&ngram);
                }
            }
        }

        for (ngram, highlighted) in analyzed {
            self.index
                .entry(ngram)
                .or_insert_with(BTreeMap::new)
                .entry(doc.id.clone())
                .or_insert_with(Vec::new)
                .search_coalesce(0, highlighted);
        }
    }

    /// Performs a search to the specification of the given query
    pub fn query(&self, query: &Query) -> Vec<SearchResult> {
        let postings = self.query_rec(query);
        self.compute_results(postings)
    }

    /// A helper method for performing a Match query
    pub fn search(&self, query: &str) -> Vec<SearchResult> {
        self.query(&Match(query))
    }

    fn postings(&self, query: &str) -> PostingsMap {
        let unique_terms: HashSet<_> = query.split_whitespace().map(str::to_lowercase).collect();
        unique_terms.into_iter().flat_map(|word| self.index.get(&word)).merge_postings()
    }

    fn query_rec(&self, query: &Query) -> PostingsMap {
        match *query {
            Match(query) => self.postings(query),
            And(queries) => {
                let postings: Vec<_> = queries.iter().map(|q| self.query_rec(q)).collect();
                postings.intersect_postings()
            }
            Or(queries) => queries.into_iter().map(|q| self.query_rec(q)).merge_postings(),
        }
    }

    fn compute_results(&self, postings: PostingsMap) -> Vec<SearchResult> {
        let mut results: Vec<_> = postings.into_iter()
                                          .map(|(doc_id, index_map)| {
                                              SearchResult::new(&self.docs[&doc_id],
                                                                index_map)
                                          })
                                          .collect();
        results.sort_by(|result1, result2| result2.score.partial_cmp(&result1.score).unwrap());
        results
    }
}

#[allow(unused_attributes)]
#[rustfmt_skip]
fn analyze_doc(doc: &str) ->
    iter::FlatMap<
        iter::Filter<
            GroupBy<bool, CharIndices, fn(&(usize, char)) -> bool>,
            fn(&(bool, Vec<(usize, char)>)) -> bool>,
        iter::Map<ops::Range<usize>, Ngrams>,
        fn((bool, Vec<(usize, char)>)) -> iter::Map<ops::Range<usize>, Ngrams>>
{
    doc.char_indices()
        .group_by(is_whitespace as fn(&(usize, char)) -> bool)
        .filter(not_whitespace as fn(&(bool, Vec<(usize, char)>)) -> bool)
        .flat_map(ngrams)
}

fn ngrams((_, chars): (bool, Vec<(usize, char)>)) -> iter::Map<ops::Range<usize>, Ngrams> {
    (1..chars.len() + 1).map(Ngrams::new(chars))
}

fn not_whitespace(&(is_whitespace, _): &(bool, Vec<(usize, char)>)) -> bool {
    !is_whitespace
}

fn is_whitespace(&(_, c): &(usize, char)) -> bool {
    c.is_whitespace()
}

struct Ngrams {
    chars: Vec<(usize, char)>,
}

impl Ngrams {
    fn new(chars: Vec<(usize, char)>) -> Ngrams {
        Ngrams { chars: chars }
    }
}

impl Fn<(usize,)> for Ngrams {
    extern "rust-call" fn call(&self, (to,): (usize,)) -> (String, (usize, usize)) {
        let word = self.chars[..to].iter().flat_map(|&(_, c)| c.to_lowercase()).collect();
        let start = self.chars[0].0;
        let (last_idx, last_char) = self.chars[to - 1];
        let finish = last_idx + last_char.len_utf8();
        (word, (start, finish))
    }
}

impl FnMut<(usize,)> for Ngrams {
    extern "rust-call" fn call_mut(&mut self, to: (usize,)) -> (String, (usize, usize)) {
        self.call(to)
    }
}

impl FnOnce<(usize,)> for Ngrams {
    type Output = (String, (usize, usize));
    extern "rust-call" fn call_once(self, to: (usize,)) -> (String, (usize, usize)) {
        self.call(to)
    }
}

#[cfg(test)]
mod test {
    use super::super::*;
    use Query::{And, Match, Or};
    use std::collections::BTreeMap;

    #[test]
    fn test_ngrams() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("1", "learn to program in rust today");
        let doc2 = Document::new("2", "what did you today do");
        index.index(doc1.clone());
        index.index(doc2.clone());
        let search_results = index.search("to");
        let expected: BTreeMap<_, _> = [(doc1.clone(), vec![(6, 8), (25, 27)]),
                                        (doc2, vec![(13, 15)])]
                                           .iter()
                                           .cloned()
                                           .collect();
        assert_eq!(search_results.len(), expected.len());
        for search_result in &search_results {
            assert_eq!(&search_result.highlights, &expected[search_result.doc])
        }
        assert_eq!("learn to program in rust today",
                   search_results.iter()
                                 .find(|search_result| search_result.doc == &doc1)
                                 .unwrap()
                                 .highlight("", ""));

    }

    #[test]
    fn test_highlight() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("2", "Won\u{2019}t this split the ecosystem? Will everyone use?");
        index.index(doc1.clone());
        let expected = "Won\u{2019}t this split the *e*cosystem? Will *e*veryone use?";
        let search_results = index.search("e");
        assert_eq!(1, search_results.len());
        assert_eq!(search_results[0].highlight("*", "*"), expected);
    }

    #[test]
    fn test_unicode() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "嗨, 您好");
        index.index(doc.clone());
        let to_search = "您";
        let search_results = index.search(to_search);
        let &SearchResult { ref doc, ref highlights, .. } = search_results.iter().next().unwrap();
        let (begin, end) = highlights[0];
        assert_eq!(&doc.content()[begin..end], to_search);
    }

    #[test]
    fn test_update_doc() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "abc åäö");
        index.index(doc);
        let doc = Document::new("0", "different");
        index.index(doc);
        let search_results = index.search("å");
        assert!(search_results.is_empty());
        assert_eq!(index.docs.len(), 1);
    }

    #[test]
    fn test_ranking() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let doc2 = Document::new("1", "beast");
        index.index(doc2);
        let search_results = index.search("be");
        assert_eq!(index.docs.len(), 2);
        // "beat" should be first, since it's a closer match
        assert_eq!(search_results[0].doc, &doc);
    }

    #[test]
    fn test_duplicate_term() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let search_results = index.search("be be");
        assert_eq!(search_results.len(), 1);
    }

    #[test]
    fn test_duplicate_term2() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "beat");
        index.index(doc.clone());
        let search_results = index.search("be b");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0].highlights, vec![(0, 2)]);
    }

    #[test]
    fn test_lowercase_search() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "BeAt");
        index.index(doc.clone());
        let search_results = index.search("bE");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0].highlights, vec![(0, 2)]);
    }

    #[test]
    fn test_lowercase_index() {
        let mut index = InvertedIndex::new();
        let doc = Document::new("0", "BeAt");
        index.index(doc.clone());
        let search_results = index.search("be");
        assert_eq!(search_results.len(), 1);
        assert_eq!(search_results[0].highlights, vec![(0, 2)]);
    }

    #[test]
    fn test_and() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("1", "learn to program in rust today");
        let doc2 = Document::new("2", "what did you today do");
        let doc3 = Document::new("3", "what did you do yesterday");
        index.index(doc1.clone());
        index.index(doc2.clone());
        index.index(doc3.clone());
        let search_results = index.query(&And(&[Match("today"), Match("you")]));
        let expected: BTreeMap<_, _> = [(doc2, vec![(9, 12), (13, 18)])]
                                           .iter()
                                           .cloned()
                                           .collect();
        assert_eq!(search_results.len(), expected.len());
        for search_result in &search_results {
            assert_eq!(&search_result.highlights, &expected[search_result.doc])
        }
    }

    #[test]
    fn test_and_or() {
        let mut index = InvertedIndex::new();
        let doc1 = Document::new("1", "learn to program in rust today");
        let doc2 = Document::new("2", "what did you today do");
        let doc3 = Document::new("3", "what did you do yesterday");
        index.index(doc1.clone());
        index.index(doc2.clone());
        index.index(doc3.clone());
        let search_results = index.query(&Or(&[Match("you"), And(&[Match("today"), Match("you")])]));
        let expected: BTreeMap<_, _> = [(doc2, vec![(9, 12), (13, 18)]), (doc3, vec![(9, 12)])]
                                           .iter()
                                           .cloned()
                                           .collect();
        assert_eq!(search_results.len(), expected.len());
        for search_result in &search_results {
            assert_eq!(&search_result.highlights, &expected[search_result.doc])
        }
    }
}
<|endoftext|>


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

extern crate core;
extern crate std;

use self::std::cell::Cell;
use self::std::hint::unreachable_unchecked;
use self::std::prelude::v1::*;
use self::std::sync::Once;
#[allow(deprecated)]
pub use self::std::sync::ONCE_INIT;

// FIXME: Replace Option<T> with MaybeUninit<T> (stable since 1.36.0)
pub struct Lazy<T: Sync>(Cell<Option<T>>, Once);

impl<T: Sync> Lazy<T> {
    #[allow(deprecated)]
    pub const INIT: Self = Lazy(Cell::new(None), ONCE_INIT);

    #[inline(always)]
    pub fn get<F>(&'static self, f: F) -> &T
    where
        F: FnOnce() -> T,
    {
        self.1.call_once(|| {
            self.0.set(Some(f()));
        });

        // `self.0` is guaranteed to be `Some` by this point
        // The `Once` will catch and propagate panics
        unsafe {
            match *self.0.as_ptr() {
                Some(ref x) => x,
                None => {
                    debug_assert!(
                        false,
                        "attempted to derefence an uninitialized lazy static. This is a bug"
                    );

                    unreachable_unchecked()
                }
            }
        }
    }
}

unsafe impl<T: Sync> Sync for Lazy<T> {}

#[macro_export]
#[doc(hidden)]
macro_rules! __lazy_static_create {
    ($NAME:ident, $T:ty) => {
        static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
    };
}
<commit_msg>Correct a typo in inline_lazy.rs<commit_after>// Copyright 2016 lazy-static.rs Developers
//
// Licensed under the Apache License, Version 2.0, <LICENSE-APACHE or
// http://apache.org/licenses/LICENSE-2.0> or the MIT license <LICENSE-MIT or
// http://opensource.org/licenses/MIT>, at your option. This file may not be
// copied, modified, or distributed except according to those terms.

extern crate core;
extern crate std;

use self::std::cell::Cell;
use self::std::hint::unreachable_unchecked;
use self::std::prelude::v1::*;
use self::std::sync::Once;
#[allow(deprecated)]
pub use self::std::sync::ONCE_INIT;

// FIXME: Replace Option<T> with MaybeUninit<T> (stable since 1.36.0)
pub struct Lazy<T: Sync>(Cell<Option<T>>, Once);

impl<T: Sync> Lazy<T> {
    #[allow(deprecated)]
    pub const INIT: Self = Lazy(Cell::new(None), ONCE_INIT);

    #[inline(always)]
    pub fn get<F>(&'static self, f: F) -> &T
    where
        F: FnOnce() -> T,
    {
        self.1.call_once(|| {
            self.0.set(Some(f()));
        });

        // `self.0` is guaranteed to be `Some` by this point
        // The `Once` will catch and propagate panics
        unsafe {
            match *self.0.as_ptr() {
                Some(ref x) => x,
                None => {
                    debug_assert!(
                        false,
                        "attempted to dereference an uninitialized lazy static. This is a bug"
                    );

                    unreachable_unchecked()
                }
            }
        }
    }
}

unsafe impl<T: Sync> Sync for Lazy<T> {}

#[macro_export]
#[doc(hidden)]
macro_rules! __lazy_static_create {
    ($NAME:ident, $T:ty) => {
        static $NAME: $crate::lazy::Lazy<$T> = $crate::lazy::Lazy::INIT;
    };
}
<|endoftext|>


<commit_before>use std::borrow::Cow;
use std::process::exit;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;

use auth_plus::authenticate;
use datatype::{AccessToken, Command, Config, Error, Event, UpdateState};
use datatype::Command::*;
use http_client::HttpClient;
use interaction_library::interpreter::Interpreter;
use ota_plus::{get_package_updates, install_package_update,
               post_installed_packages, send_install_report};

pub struct Env<'a> {
    pub config:       Config,
    pub access_token: Option<Cow<'a, AccessToken>>,
    pub http_client:  Arc<Mutex<HttpClient>>,
}

// This macro partially applies the config, http client and token to the
// passed in functions.
macro_rules! partial_apply {
    ([ $( $fun0: ident ),* ], // Functions of arity 0,
     [ $( $fun1: ident ),* ], // arity 1,
     [ $( $fun2: ident ),* ], // and arity 2.
     $config: expr, $client: expr, $token: expr) => {
        $(let $fun0 = ||           $fun0(&$config, &mut *$client.lock().unwrap(), $token);)*
        $(let $fun1 = |arg|        $fun1(&$config, &mut *$client.lock().unwrap(), $token, &arg);)*
        $(let $fun2 = |arg1, arg2| $fun2(&$config, &mut *$client.lock().unwrap(), $token, &arg1, &arg2);)*
    }
}

fn interpreter(env: &mut Env, cmd: Command, tx: &Sender<Event>) -> Result<(), Error> {

    Ok(if let Some(token) = env.access_token.to_owned() {

        let client_clone = env.http_client.clone();

        partial_apply!(
            [get_package_updates, post_installed_packages],
            [send_install_report],
            [install_package_update], &env.config, client_clone, &token);

        match cmd {

            Authenticate(_)       => (), // Already authenticated.

            AcceptUpdate(ref id)  => {
                try!(tx.send(Event::UpdateStateChanged(id.clone(), UpdateState::Downloading)));
                let report = try!(install_package_update(id.to_owned(), tx.to_owned()));
                try!(send_install_report(report.clone()));
                info!("Update finished. Report sent: {:?}", report)
            }

            GetPendingUpdates     => {
                let updates = try!(get_package_updates());
                let update_events: Vec<Event> = updates
                    .iter()
                    .map(|id| Event::NewUpdateAvailable(id.clone()))
                    .collect();
                info!("New package updates available: {:?}", update_events);
                try!(tx.send(Event::Batch(update_events)))
            }

            ListInstalledPackages => {
                let pkgs = try!(env.config.ota.package_manager.installed_packages());
                try!(tx.send(Event::FoundInstalledPackages(pkgs.clone())))
            }

            PostInstalledPackages => {
                try!(post_installed_packages());
                info!("Posted installed packages to the server.")
            }

            Shutdown              => exit(0)
        }

    } else {

        match cmd {

            Authenticate(_)               => {
                // XXX: partially apply?
                let client_clone = env.http_client.clone();
                let mut client = client_clone.lock().unwrap();
                let token = try!(authenticate(&env.config.auth, &mut *client));
                env.access_token = Some(token.into())
            }

            Shutdown                      => exit(0),

            AcceptUpdate(_)       |
            GetPendingUpdates     |
            ListInstalledPackages |
            PostInstalledPackages         =>
                tx.send(Event::NotAuthenticated)
                  .unwrap_or(error!("interpreter: send failed."))
            }

    })

}

pub struct OurInterpreter;

impl<'a> Interpreter<Env<'a>, Command, Event> for OurInterpreter {

    fn interpret(env: &mut Env, cmd: Command, tx: Sender<Event>) {
        interpreter(env, cmd, &tx)
            .unwrap_or_else(|err| tx.send(Event::Error(format!("{}", err)))
                            .unwrap_or(error!("interpret: send failed.")))
    }

}
<commit_msg>Log which commands get interpreted.<commit_after>use std::borrow::Cow;
use std::process::exit;
use std::sync::{Arc, Mutex};
use std::sync::mpsc::Sender;

use auth_plus::authenticate;
use datatype::{AccessToken, Command, Config, Error, Event, UpdateState};
use datatype::Command::*;
use http_client::HttpClient;
use interaction_library::interpreter::Interpreter;
use ota_plus::{get_package_updates, install_package_update,
               post_installed_packages, send_install_report};

pub struct Env<'a> {
    pub config:       Config,
    pub access_token: Option<Cow<'a, AccessToken>>,
    pub http_client:  Arc<Mutex<HttpClient>>,
}

// This macro partially applies the config, http client and token to the
// passed in functions.
macro_rules! partial_apply {
    ([ $( $fun0: ident ),* ], // Functions of arity 0,
     [ $( $fun1: ident ),* ], // arity 1,
     [ $( $fun2: ident ),* ], // and arity 2.
     $config: expr, $client: expr, $token: expr) => {
        $(let $fun0 = ||           $fun0(&$config, &mut *$client.lock().unwrap(), $token);)*
        $(let $fun1 = |arg|        $fun1(&$config, &mut *$client.lock().unwrap(), $token, &arg);)*
        $(let $fun2 = |arg1, arg2| $fun2(&$config, &mut *$client.lock().unwrap(), $token, &arg1, &arg2);)*
    }
}

fn interpreter(env: &mut Env, cmd: Command, tx: &Sender<Event>) -> Result<(), Error> {

    Ok(if let Some(token) = env.access_token.to_owned() {

        let client_clone = env.http_client.clone();

        partial_apply!(
            [get_package_updates, post_installed_packages],
            [send_install_report],
            [install_package_update], &env.config, client_clone, &token);

        match cmd {

            Authenticate(_)       => (), // Already authenticated.

            AcceptUpdate(ref id)  => {
                try!(tx.send(Event::UpdateStateChanged(id.clone(), UpdateState::Downloading)));
                let report = try!(install_package_update(id.to_owned(), tx.to_owned()));
                try!(send_install_report(report.clone()));
                info!("Update finished. Report sent: {:?}", report)
            }

            GetPendingUpdates     => {
                let updates = try!(get_package_updates());
                let update_events: Vec<Event> = updates
                    .iter()
                    .map(|id| Event::NewUpdateAvailable(id.clone()))
                    .collect();
                info!("New package updates available: {:?}", update_events);
                try!(tx.send(Event::Batch(update_events)))
            }

            ListInstalledPackages => {
                let pkgs = try!(env.config.ota.package_manager.installed_packages());
                try!(tx.send(Event::FoundInstalledPackages(pkgs.clone())))
            }

            PostInstalledPackages => {
                try!(post_installed_packages());
                info!("Posted installed packages to the server.")
            }

            Shutdown              => exit(0)
        }

    } else {

        match cmd {

            Authenticate(_)               => {
                // XXX: partially apply?
                let client_clone = env.http_client.clone();
                let mut client = client_clone.lock().unwrap();
                let token = try!(authenticate(&env.config.auth, &mut *client));
                env.access_token = Some(token.into())
            }

            Shutdown                      => exit(0),

            AcceptUpdate(_)       |
            GetPendingUpdates     |
            ListInstalledPackages |
            PostInstalledPackages         =>
                tx.send(Event::NotAuthenticated)
                  .unwrap_or(error!("interpreter: send failed."))
            }

    })

}

pub struct OurInterpreter;

impl<'a> Interpreter<Env<'a>, Command, Event> for OurInterpreter {

    fn interpret(env: &mut Env, cmd: Command, tx: Sender<Event>) {

        info!("Interpreting: {:?}", cmd);

        interpreter(env, cmd, &tx)
            .unwrap_or_else(|err| tx.send(Event::Error(format!("{}", err)))
                            .unwrap_or(error!("interpret: send failed.")))
    }

}
<|endoftext|>


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

use ::math::div_round_up;
use super::plumbing::*;
use super::*;

#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug, Clone)]
pub struct Chunks
    where I: IndexedParallelIterator
{
    size: usize,
    i: I,
}

/// Create a new `Chunks` iterator
///
/// NB: a free fn because it is NOT part of the end-user API.
pub fn new(i: I, size: usize) -> Chunks
    where I: IndexedParallelIterator
{
    Chunks { i: i, size: size }
}

impl ParallelIterator for Chunks
    where I: IndexedParallelIterator
{
    type Item = Vec;

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: Consumer<Vec>
    {
        bridge(self, consumer)
    }

    fn opt_len(&self) -> Option<usize> {
        Some(self.len())
    }
}

impl IndexedParallelIterator for Chunks
    where I: IndexedParallelIterator
{
    fn drive<C>(self, consumer: C) -> C::Result
        where C: Consumer<Self::Item>
    {
        bridge(self, consumer)
    }

    fn len(&self) -> usize {
        div_round_up(self.i.len(), self.size)
    }

    fn with_producer<CB>(self, callback: CB) -> CB::Output
        where CB: ProducerCallback<Self::Item>
    {
        let len = self.i.len();
        return self.i.with_producer(Callback {
            size: self.size,
            len: len,
            callback: callback,
        });

        struct Callback<CB> {
            size: usize,
            len: usize,
            callback: CB,
        }

        impl<T, CB> ProducerCallback<T> for Callback<CB>
            where CB: ProducerCallback<Vec<T>>
        {
            type Output = CB::Output;

            fn callback
(self, base: P) -> CB::Output
                where P: Producer<Item = T>
            {
                self.callback.callback(ChunkProducer {
                    chunk_size: self.size,
                    len: self.len,
                    base: base,
                })
            }
        }
    }
}

struct ChunkProducer

    where P: Producer
{
    chunk_size: usize,
    len: usize,
    base: P,
}

impl
 Producer for ChunkProducer

    where P: Producer
{
    type Item = Vec
;
    type IntoIter = ChunkSeq
;

    fn into_iter(self) -> Self::IntoIter {
        ChunkSeq {
            chunk_size: self.chunk_size,
            len: self.len,
            inner: if self.len > 0 {
                Some(self.base)
            } else {
                None
            }
        }
    }

    fn split_at(self, index: usize) -> (Self, Self) {
        let elem_index = min(index * self.chunk_size, self.len);
        let (left, right) = self.base.split_at(elem_index);
        (ChunkProducer {
            chunk_size: self.chunk_size,
            len: elem_index,
            base: left,
        },
        ChunkProducer {
            chunk_size: self.chunk_size,
            len: self.len - elem_index,
            base: right,
        })
    }

    fn min_len(&self) -> usize {
        div_round_up(self.base.min_len(), self.chunk_size)
    }

    fn max_len(&self) -> usize {
        self.base.max_len() / self.chunk_size
    }
}

struct ChunkSeq
 {
    chunk_size: usize,
    len: usize,
    inner: Option
,
}

impl
 Iterator for ChunkSeq

    where P: Producer
{
    type Item = Vec
;

    fn next(&mut self) -> Option<Self::Item> {
        match self.inner.take() {
            Some(producer) => if self.len > self.chunk_size {
                let (left, right) = producer.split_at(self.chunk_size);
                self.inner = Some(right);
                self.len -= self.chunk_size;
                Some(left.into_iter().collect())
            } else {
                debug_assert!(self.len > 0);
                self.len = 0;
                Some(producer.into_iter().collect())
            },
            _ => None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }
}

impl
 ExactSizeIterator for ChunkSeq

    where P: Producer
{
    #[inline]
    fn len(&self) -> usize {
        div_round_up(self.len, self.chunk_size)
    }
}

impl
 DoubleEndedIterator for ChunkSeq

    where P: Producer
{
    fn next_back(&mut self) -> Option<Self::Item> {
        match self.inner.take() {
            Some(producer) => if self.len > self.chunk_size {
                let mut size = self.len % self.chunk_size;
                if size == 0 {
                    size = self.chunk_size;
                }
                let (left, right) = producer.split_at(self.len - size);
                self.inner = Some(left);
                self.len -= size;
                Some(right.into_iter().collect())
            } else {
                debug_assert!(self.len > 0);
                self.len = 0;
                Some(producer.into_iter().collect())
            },
            _ => None
        }
    }
}
<commit_msg>Document iter::Chunks<commit_after>use std::cmp::min;

use ::math::div_round_up;
use super::plumbing::*;
use super::*;

/// `Chunks` is an iterator that groups elements of an underlying iterator.
///
/// This struct is created by the [`chunks()`] method on [`IndexedParallelIterator`]
///
/// [`chunks()`]: trait.IndexedParallelIterator.html#method.chunks
/// [`IndexedParallelIterator`]: trait.IndexedParallelIterator.html
#[must_use = "iterator adaptors are lazy and do nothing unless consumed"]
#[derive(Debug, Clone)]
pub struct Chunks
    where I: IndexedParallelIterator
{
    size: usize,
    i: I,
}

/// Create a new `Chunks` iterator
///
/// NB: a free fn because it is NOT part of the end-user API.
pub fn new(i: I, size: usize) -> Chunks
    where I: IndexedParallelIterator
{
    Chunks { i: i, size: size }
}

impl ParallelIterator for Chunks
    where I: IndexedParallelIterator
{
    type Item = Vec;

    fn drive_unindexed<C>(self, consumer: C) -> C::Result
        where C: Consumer<Vec>
    {
        bridge(self, consumer)
    }

    fn opt_len(&self) -> Option<usize> {
        Some(self.len())
    }
}

impl IndexedParallelIterator for Chunks
    where I: IndexedParallelIterator
{
    fn drive<C>(self, consumer: C) -> C::Result
        where C: Consumer<Self::Item>
    {
        bridge(self, consumer)
    }

    fn len(&self) -> usize {
        div_round_up(self.i.len(), self.size)
    }

    fn with_producer<CB>(self, callback: CB) -> CB::Output
        where CB: ProducerCallback<Self::Item>
    {
        let len = self.i.len();
        return self.i.with_producer(Callback {
            size: self.size,
            len: len,
            callback: callback,
        });

        struct Callback<CB> {
            size: usize,
            len: usize,
            callback: CB,
        }

        impl<T, CB> ProducerCallback<T> for Callback<CB>
            where CB: ProducerCallback<Vec<T>>
        {
            type Output = CB::Output;

            fn callback
(self, base: P) -> CB::Output
                where P: Producer<Item = T>
            {
                self.callback.callback(ChunkProducer {
                    chunk_size: self.size,
                    len: self.len,
                    base: base,
                })
            }
        }
    }
}

struct ChunkProducer

    where P: Producer
{
    chunk_size: usize,
    len: usize,
    base: P,
}

impl
 Producer for ChunkProducer

    where P: Producer
{
    type Item = Vec
;
    type IntoIter = ChunkSeq
;

    fn into_iter(self) -> Self::IntoIter {
        ChunkSeq {
            chunk_size: self.chunk_size,
            len: self.len,
            inner: if self.len > 0 {
                Some(self.base)
            } else {
                None
            }
        }
    }

    fn split_at(self, index: usize) -> (Self, Self) {
        let elem_index = min(index * self.chunk_size, self.len);
        let (left, right) = self.base.split_at(elem_index);
        (ChunkProducer {
            chunk_size: self.chunk_size,
            len: elem_index,
            base: left,
        },
        ChunkProducer {
            chunk_size: self.chunk_size,
            len: self.len - elem_index,
            base: right,
        })
    }

    fn min_len(&self) -> usize {
        div_round_up(self.base.min_len(), self.chunk_size)
    }

    fn max_len(&self) -> usize {
        self.base.max_len() / self.chunk_size
    }
}

struct ChunkSeq
 {
    chunk_size: usize,
    len: usize,
    inner: Option
,
}

impl
 Iterator for ChunkSeq

    where P: Producer
{
    type Item = Vec
;

    fn next(&mut self) -> Option<Self::Item> {
        match self.inner.take() {
            Some(producer) => if self.len > self.chunk_size {
                let (left, right) = producer.split_at(self.chunk_size);
                self.inner = Some(right);
                self.len -= self.chunk_size;
                Some(left.into_iter().collect())
            } else {
                debug_assert!(self.len > 0);
                self.len = 0;
                Some(producer.into_iter().collect())
            },
            _ => None
        }
    }

    fn size_hint(&self) -> (usize, Option<usize>) {
        let len = self.len();
        (len, Some(len))
    }
}

impl
 ExactSizeIterator for ChunkSeq

    where P: Producer
{
    #[inline]
    fn len(&self) -> usize {
        div_round_up(self.len, self.chunk_size)
    }
}

impl
 DoubleEndedIterator for ChunkSeq

    where P: Producer
{
    fn next_back(&mut self) -> Option<Self::Item> {
        match self.inner.take() {
            Some(producer) => if self.len > self.chunk_size {
                let mut size = self.len % self.chunk_size;
                if size == 0 {
                    size = self.chunk_size;
                }
                let (left, right) = producer.split_at(self.len - size);
                self.inner = Some(left);
                self.len -= size;
                Some(right.into_iter().collect())
            } else {
                debug_assert!(self.len > 0);
                self.len = 0;
                Some(producer.into_iter().collect())
            },
            _ => None
        }
    }
}
<|endoftext|>


<commit_before>/*!
A push button is a rectangle containing an application-defined text label, an icon, or a bitmap
that indicates what the button does when the user selects it.
*/

use winapi::um::winuser::{WS_VISIBLE, WS_DISABLED};
use winapi::um::commctrl::{PBS_MARQUEE, PBS_VERTICAL};
use crate::win32::window_helper as wh;
use crate::win32::base_helper::check_hwnd;
use crate::NwgError;
use super::{ControlHandle, ControlBase};
use std::ops::Range;

const NOT_BOUND: &'static str = "Progress bar is not yet bound to a winapi object";
const BAD_HANDLE: &'static str = "INTERNAL ERROR: Progress bar handle is not HWND!";

bitflags! {
    pub struct ProgressBarFlags: u32 {
        const VISIBLE = WS_VISIBLE;
        const DISABLED = WS_DISABLED;
        const VERTICAL = PBS_VERTICAL;
        const MARQUEE = PBS_MARQUEE;
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum ProgressBarState {
    Normal,
    Error,
    Paused
}

/**
A progress bar is a window that an application can use to indicate the progress of a lengthy operation.

Requires the `progress-bar` feature.

**Builder parameters:**
  * `parent`:         **Required.** The progress bar parent container.
  * `size`:           The progress bar size.
  * `position`:       The progress bar position.
  * `state`:          The initial state of the progress bar.
  * `step`:           The value in which the progress bar value increase when `advance` is used.
  * `pos`:            The initial value of the progress bar.
  * `range`:          The minimum and maximum value in the progress bar.
  * `enabled`:        If the progress bar is enabled.
  * `flags`:          A combination of the ProgressBarFlags values.
  * `marquee`:        Enable of disable the marquee animation (only used with the MARQUEE flags)
  * `marquee_update`: The update interval of the marquee mode

**Control events:**
  * `MousePress(_)`: Generic mouse press events on the progress bar
  * `OnMouseMove`: Generic mouse mouse event
  * `OnMouseWheel`: Generic mouse wheel event

```rust
use native_windows_gui as nwg;
fn build_progress_bar(bar: &mut nwg::ProgressBar, window: &nwg::Window) {
    nwg::ProgressBar::builder()
        .state(nwg::ProgressBarState::Paused)
        .step(10)
        .range(0..100)
        .parent(window)
        .build(bar);
}
```

*/
#[derive(Default, PartialEq, Eq)]
pub struct ProgressBar {
    pub handle: ControlHandle
}

impl ProgressBar {

    pub fn builder() -> ProgressBarBuilder {
        ProgressBarBuilder {
            size: (100, 40),
            position: (0, 0),
            flags: None,
            state: ProgressBarState::Normal,
            step: 1,
            pos: 0,
            range: 0..100,
            marquee_enable: false,
            marquee_update: 0,
            parent: None
        }
    }

    /// Return the current state of the progress bar
    pub fn state(&self) -> ProgressBarState {
        use winapi::um::commctrl::{PBM_GETSTATE, PBST_NORMAL, PBST_ERROR, PBST_PAUSED};

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);

        match wh::send_message(handle, PBM_GETSTATE, 0, 0) as i32 {
            PBST_NORMAL => ProgressBarState::Normal,
            PBST_ERROR => ProgressBarState::Error,
            PBST_PAUSED => ProgressBarState::Paused,
            _ => panic!("Unkown progress bar state")
        }
    }

    /// Set the state of the progress bar
    pub fn set_state(&self, state: ProgressBarState) {
        use winapi::um::commctrl::{PBM_SETSTATE, PBST_NORMAL, PBST_ERROR, PBST_PAUSED};
        use winapi::shared::minwindef::WPARAM;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);

        let state = match state {
            ProgressBarState::Normal => PBST_NORMAL,
            ProgressBarState::Error => PBST_ERROR,
            ProgressBarState::Paused => PBST_PAUSED
        };

        wh::send_message(handle, PBM_SETSTATE, state as WPARAM, 0);
    }

    /// Increase the bar value by the step value
    pub fn advance(&self) {
        use winapi::um::commctrl::PBM_STEPIT;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_STEPIT, 0, 0);
    }

    /// Increase the bar value by a value
    pub fn advance_delta(&self, v: u32) {
        use winapi::um::commctrl::PBM_DELTAPOS;
        use winapi::shared::minwindef::WPARAM;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_DELTAPOS, v as WPARAM, 0);
    }

    /// Return the step of the progress bar.
    pub fn step(&self) -> u32 {
        use winapi::um::commctrl::PBM_GETSTEP;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_GETSTEP, 0, 0) as u32
    }

    /// Set the step of the progress bar.
    pub fn set_step(&self, s: u32) {
        use winapi::um::commctrl::PBM_SETSTEP;
        use winapi::shared::minwindef::WPARAM;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_SETSTEP, s as WPARAM, 0);
    }

    /// Return the position of the progress bar.
    pub fn pos(&self) -> u32 {
        use winapi::um::commctrl::PBM_GETPOS;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_GETPOS, 0, 0) as u32
    }

    /// Set the position of the progress bar. If the value is outside of range
    /// sets the value to the nearest bound.
    pub fn set_pos(&self, p: u32) {
        use winapi::um::commctrl::PBM_SETPOS;
        use winapi::shared::minwindef::WPARAM;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_SETPOS, p as WPARAM, 0);
    }

    /// Get the range of the progress bar
    pub fn range(&self) -> Range<u32> {
        use winapi::um::commctrl::PBM_GETRANGE;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);

        let low = wh::send_message(handle, PBM_GETRANGE, 1, 0) as u32;
        let high = wh::send_message(handle, PBM_GETRANGE, 0, 0) as u32;

        low..high
    }

    /// Set the range of the progress bar
    pub fn set_range(&self, range: Range<u32>) {
        use winapi::um::commctrl::PBM_SETRANGE32;
        use winapi::shared::minwindef::{WPARAM, LPARAM};

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_SETRANGE32, range.start as WPARAM, range.end as LPARAM);
    }

    /// Set the progress bar marquee mode
    pub fn set_marquee(&self, enable: bool, update_interval: u32) {
        use winapi::shared::minwindef::{LPARAM, WPARAM};
        use winapi::um::commctrl::PBM_SETMARQUEE;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_SETMARQUEE, enable as WPARAM, update_interval as LPARAM);
    }

    /// Updates the flags of the progress bar.
    pub fn add_flags(&self, styles: ProgressBarFlags) {
        use winapi::um::winuser::GWL_STYLE;

        let styles = styles.bits() as u32;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        let active_styles = wh::get_style(handle);

        wh::set_style(handle, active_styles | styles);
    }

    /// Removes flags from the progress bar.
    pub fn remove_flags(&self, styles: ProgressBarFlags) {
        use winapi::um::winuser::GWL_STYLE;

        let styles = styles.bits() as u32;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        let active_styles = wh::get_style(handle);

        wh::set_style(handle, active_styles & !styles);
    }

    /// Return true if the control currently has the keyboard focus
    pub fn focus(&self) -> bool {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_focus(handle) }
    }

    /// Set the keyboard focus on the button.
    pub fn set_focus(&self) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_focus(handle); }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_window_enabled(handle) }
    }

    /// Enable or disable the control
    pub fn set_enabled(&self, v: bool) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_window_enabled(handle, v) }
    }

    /// Return true if the control is visible to the user. Will return true even if the
    /// control is outside of the parent client view (ex: at the position (10000, 10000))
    pub fn visible(&self) -> bool {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_window_visibility(handle) }
    }

    /// Show or hide the control to the user
    pub fn set_visible(&self, v: bool) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_window_visibility(handle, v) }
    }

    /// Return the size of the button in the parent window
    pub fn size(&self) -> (u32, u32) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_window_size(handle) }
    }

    /// Set the size of the button in the parent window
    pub fn set_size(&self, x: u32, y: u32) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_window_size(handle, x, y, false) }
    }

    /// Return the position of the button in the parent window
    pub fn position(&self) -> (i32, i32) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_window_position(handle) }
    }

    /// Set the position of the button in the parent window
    pub fn set_position(&self, x: i32, y: i32) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_window_position(handle, x, y) }
    }

    /// Winapi class name used during control creation
    pub fn class_name(&self) -> &'static str {
        "msctls_progress32"
    }

    /// Winapi base flags used during window creation
    pub fn flags(&self) -> u32 {
        ::winapi::um::winuser::WS_VISIBLE
    }

    /// Winapi flags required by the control
    pub fn forced_flags(&self) -> u32 {
        use winapi::um::winuser::{WS_CHILD};

        WS_CHILD
    }

}

impl Drop for ProgressBar {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct ProgressBarBuilder {
    size: (i32, i32),
    position: (i32, i32),
    flags: Option<ProgressBarFlags>,
    state: ProgressBarState,
    step: u32,
    pos: u32,
    range: Range<u32>,
    marquee_enable: bool,
    marquee_update: u32,
    parent: Option<ControlHandle>
}

impl ProgressBarBuilder {

    pub fn flags(mut self, flags: ProgressBarFlags) -> ProgressBarBuilder {
        self.flags = Some(flags);
        self
    }

    pub fn size(mut self, size: (i32, i32)) -> ProgressBarBuilder {
        self.size = size;
        self
    }

    pub fn position(mut self, pos: (i32, i32)) -> ProgressBarBuilder {
        self.position = pos;
        self
    }

    pub fn state(mut self, state: ProgressBarState) -> ProgressBarBuilder {
        self.state = state;
        self
    }

    pub fn step(mut self, step: u32) -> ProgressBarBuilder {
        self.step = step;
        self
    }

    pub fn pos(mut self, pos: u32) -> ProgressBarBuilder {
        self.pos = pos;
        self
    }

    pub fn range(mut self, range: Range<u32>) -> ProgressBarBuilder {
        self.range = range;
        self
    }

    pub fn marquee(mut self, enable: bool) -> ProgressBarBuilder {
        self.marquee_enable = enable;
        self
    }

    pub fn marquee_update(mut self, update_interval: u32) -> ProgressBarBuilder {
        self.marquee_update = update_interval;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ProgressBarBuilder {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, out: &mut ProgressBar) -> Result<(), NwgError> {
        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());

        let parent = match self.parent {
            Some(p) => Ok(p),
            None => Err(NwgError::no_parent("Progress Bar"))
        }?;

        out.handle = ControlBase::build_hwnd()
            .class_name(out.class_name())
            .forced_flags(out.forced_flags())
            .flags(flags)
            .size(self.size)
            .position(self.position)
            .parent(Some(parent))
            .build()?;

        out.set_state(self.state);
        out.set_step(self.step);
        out.set_pos(self.pos);
        out.set_range(self.range);
        out.set_marquee(self.marquee_enable, self.marquee_update);

        Ok(())
    }

}
<commit_msg>remove old flags<commit_after>/*!
A push button is a rectangle containing an application-defined text label, an icon, or a bitmap
that indicates what the button does when the user selects it.
*/

use winapi::um::winuser::{WS_VISIBLE, WS_DISABLED};
use winapi::um::commctrl::{PBS_MARQUEE, PBS_VERTICAL};
use crate::win32::window_helper as wh;
use crate::win32::base_helper::check_hwnd;
use crate::NwgError;
use super::{ControlHandle, ControlBase};
use std::ops::Range;

const NOT_BOUND: &'static str = "Progress bar is not yet bound to a winapi object";
const BAD_HANDLE: &'static str = "INTERNAL ERROR: Progress bar handle is not HWND!";

bitflags! {
    pub struct ProgressBarFlags: u32 {
        const VISIBLE = WS_VISIBLE;
        const DISABLED = WS_DISABLED;
        const VERTICAL = PBS_VERTICAL;
        const MARQUEE = PBS_MARQUEE;
    }
}

#[derive(Copy, Clone, Debug, PartialEq, Eq)]
#[repr(u8)]
pub enum ProgressBarState {
    Normal,
    Error,
    Paused
}

/**
A progress bar is a window that an application can use to indicate the progress of a lengthy operation.

Requires the `progress-bar` feature.

**Builder parameters:**
  * `parent`:         **Required.** The progress bar parent container.
  * `size`:           The progress bar size.
  * `position`:       The progress bar position.
  * `state`:          The initial state of the progress bar.
  * `step`:           The value in which the progress bar value increase when `advance` is used.
  * `pos`:            The initial value of the progress bar.
  * `range`:          The minimum and maximum value in the progress bar.
  * `enabled`:        If the progress bar is enabled.
  * `flags`:          A combination of the ProgressBarFlags values.
  * `marquee`:        Enable of disable the marquee animation (only used with the MARQUEE flags)
  * `marquee_update`: The update interval of the marquee mode

**Control events:**
  * `MousePress(_)`: Generic mouse press events on the progress bar
  * `OnMouseMove`: Generic mouse mouse event
  * `OnMouseWheel`: Generic mouse wheel event

```rust
use native_windows_gui as nwg;
fn build_progress_bar(bar: &mut nwg::ProgressBar, window: &nwg::Window) {
    nwg::ProgressBar::builder()
        .state(nwg::ProgressBarState::Paused)
        .step(10)
        .range(0..100)
        .parent(window)
        .build(bar);
}
```

*/
#[derive(Default, PartialEq, Eq)]
pub struct ProgressBar {
    pub handle: ControlHandle
}

impl ProgressBar {

    pub fn builder() -> ProgressBarBuilder {
        ProgressBarBuilder {
            size: (100, 40),
            position: (0, 0),
            flags: None,
            state: ProgressBarState::Normal,
            step: 1,
            pos: 0,
            range: 0..100,
            marquee_enable: false,
            marquee_update: 0,
            parent: None
        }
    }

    /// Return the current state of the progress bar
    pub fn state(&self) -> ProgressBarState {
        use winapi::um::commctrl::{PBM_GETSTATE, PBST_NORMAL, PBST_ERROR, PBST_PAUSED};

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);

        match wh::send_message(handle, PBM_GETSTATE, 0, 0) as i32 {
            PBST_NORMAL => ProgressBarState::Normal,
            PBST_ERROR => ProgressBarState::Error,
            PBST_PAUSED => ProgressBarState::Paused,
            _ => panic!("Unkown progress bar state")
        }
    }

    /// Set the state of the progress bar
    pub fn set_state(&self, state: ProgressBarState) {
        use winapi::um::commctrl::{PBM_SETSTATE, PBST_NORMAL, PBST_ERROR, PBST_PAUSED};
        use winapi::shared::minwindef::WPARAM;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);

        let state = match state {
            ProgressBarState::Normal => PBST_NORMAL,
            ProgressBarState::Error => PBST_ERROR,
            ProgressBarState::Paused => PBST_PAUSED
        };

        wh::send_message(handle, PBM_SETSTATE, state as WPARAM, 0);
    }

    /// Increase the bar value by the step value
    pub fn advance(&self) {
        use winapi::um::commctrl::PBM_STEPIT;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_STEPIT, 0, 0);
    }

    /// Increase the bar value by a value
    pub fn advance_delta(&self, v: u32) {
        use winapi::um::commctrl::PBM_DELTAPOS;
        use winapi::shared::minwindef::WPARAM;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_DELTAPOS, v as WPARAM, 0);
    }

    /// Return the step of the progress bar.
    pub fn step(&self) -> u32 {
        use winapi::um::commctrl::PBM_GETSTEP;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_GETSTEP, 0, 0) as u32
    }

    /// Set the step of the progress bar.
    pub fn set_step(&self, s: u32) {
        use winapi::um::commctrl::PBM_SETSTEP;
        use winapi::shared::minwindef::WPARAM;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_SETSTEP, s as WPARAM, 0);
    }

    /// Return the position of the progress bar.
    pub fn pos(&self) -> u32 {
        use winapi::um::commctrl::PBM_GETPOS;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_GETPOS, 0, 0) as u32
    }

    /// Set the position of the progress bar. If the value is outside of range
    /// sets the value to the nearest bound.
    pub fn set_pos(&self, p: u32) {
        use winapi::um::commctrl::PBM_SETPOS;
        use winapi::shared::minwindef::WPARAM;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_SETPOS, p as WPARAM, 0);
    }

    /// Get the range of the progress bar
    pub fn range(&self) -> Range<u32> {
        use winapi::um::commctrl::PBM_GETRANGE;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);

        let low = wh::send_message(handle, PBM_GETRANGE, 1, 0) as u32;
        let high = wh::send_message(handle, PBM_GETRANGE, 0, 0) as u32;

        low..high
    }

    /// Set the range of the progress bar
    pub fn set_range(&self, range: Range<u32>) {
        use winapi::um::commctrl::PBM_SETRANGE32;
        use winapi::shared::minwindef::{WPARAM, LPARAM};

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_SETRANGE32, range.start as WPARAM, range.end as LPARAM);
    }

    /// Set the progress bar marquee mode
    pub fn set_marquee(&self, enable: bool, update_interval: u32) {
        use winapi::shared::minwindef::{LPARAM, WPARAM};
        use winapi::um::commctrl::PBM_SETMARQUEE;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        wh::send_message(handle, PBM_SETMARQUEE, enable as WPARAM, update_interval as LPARAM);
    }

    /// Updates the flags of the progress bar.
    pub fn add_flags(&self, styles: ProgressBarFlags) {
        let styles = styles.bits() as u32;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        let active_styles = wh::get_style(handle);

        wh::set_style(handle, active_styles | styles);
    }

    /// Removes flags from the progress bar.
    pub fn remove_flags(&self, styles: ProgressBarFlags) {
        let styles = styles.bits() as u32;

        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        let active_styles = wh::get_style(handle);

        wh::set_style(handle, active_styles & !styles);
    }

    /// Return true if the control currently has the keyboard focus
    pub fn focus(&self) -> bool {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_focus(handle) }
    }

    /// Set the keyboard focus on the button.
    pub fn set_focus(&self) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_focus(handle); }
    }

    /// Return true if the control user can interact with the control, return false otherwise
    pub fn enabled(&self) -> bool {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_window_enabled(handle) }
    }

    /// Enable or disable the control
    pub fn set_enabled(&self, v: bool) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_window_enabled(handle, v) }
    }

    /// Return true if the control is visible to the user. Will return true even if the
    /// control is outside of the parent client view (ex: at the position (10000, 10000))
    pub fn visible(&self) -> bool {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_window_visibility(handle) }
    }

    /// Show or hide the control to the user
    pub fn set_visible(&self, v: bool) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_window_visibility(handle, v) }
    }

    /// Return the size of the button in the parent window
    pub fn size(&self) -> (u32, u32) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_window_size(handle) }
    }

    /// Set the size of the button in the parent window
    pub fn set_size(&self, x: u32, y: u32) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_window_size(handle, x, y, false) }
    }

    /// Return the position of the button in the parent window
    pub fn position(&self) -> (i32, i32) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::get_window_position(handle) }
    }

    /// Set the position of the button in the parent window
    pub fn set_position(&self, x: i32, y: i32) {
        let handle = check_hwnd(&self.handle, NOT_BOUND, BAD_HANDLE);
        unsafe { wh::set_window_position(handle, x, y) }
    }

    /// Winapi class name used during control creation
    pub fn class_name(&self) -> &'static str {
        "msctls_progress32"
    }

    /// Winapi base flags used during window creation
    pub fn flags(&self) -> u32 {
        ::winapi::um::winuser::WS_VISIBLE
    }

    /// Winapi flags required by the control
    pub fn forced_flags(&self) -> u32 {
        use winapi::um::winuser::{WS_CHILD};

        WS_CHILD
    }

}

impl Drop for ProgressBar {
    fn drop(&mut self) {
        self.handle.destroy();
    }
}

pub struct ProgressBarBuilder {
    size: (i32, i32),
    position: (i32, i32),
    flags: Option<ProgressBarFlags>,
    state: ProgressBarState,
    step: u32,
    pos: u32,
    range: Range<u32>,
    marquee_enable: bool,
    marquee_update: u32,
    parent: Option<ControlHandle>
}

impl ProgressBarBuilder {

    pub fn flags(mut self, flags: ProgressBarFlags) -> ProgressBarBuilder {
        self.flags = Some(flags);
        self
    }

    pub fn size(mut self, size: (i32, i32)) -> ProgressBarBuilder {
        self.size = size;
        self
    }

    pub fn position(mut self, pos: (i32, i32)) -> ProgressBarBuilder {
        self.position = pos;
        self
    }

    pub fn state(mut self, state: ProgressBarState) -> ProgressBarBuilder {
        self.state = state;
        self
    }

    pub fn step(mut self, step: u32) -> ProgressBarBuilder {
        self.step = step;
        self
    }

    pub fn pos(mut self, pos: u32) -> ProgressBarBuilder {
        self.pos = pos;
        self
    }

    pub fn range(mut self, range: Range<u32>) -> ProgressBarBuilder {
        self.range = range;
        self
    }

    pub fn marquee(mut self, enable: bool) -> ProgressBarBuilder {
        self.marquee_enable = enable;
        self
    }

    pub fn marquee_update(mut self, update_interval: u32) -> ProgressBarBuilder {
        self.marquee_update = update_interval;
        self
    }

    pub fn parent<C: Into<ControlHandle>>(mut self, p: C) -> ProgressBarBuilder {
        self.parent = Some(p.into());
        self
    }

    pub fn build(self, out: &mut ProgressBar) -> Result<(), NwgError> {
        let flags = self.flags.map(|f| f.bits()).unwrap_or(out.flags());

        let parent = match self.parent {
            Some(p) => Ok(p),
            None => Err(NwgError::no_parent("Progress Bar"))
        }?;

        out.handle = ControlBase::build_hwnd()
            .class_name(out.class_name())
            .forced_flags(out.forced_flags())
            .flags(flags)
            .size(self.size)
            .position(self.position)
            .parent(Some(parent))
            .build()?;

        out.set_state(self.state);
        out.set_step(self.step);
        out.set_pos(self.pos);
        out.set_range(self.range);
        out.set_marquee(self.marquee_enable, self.marquee_update);

        Ok(())
    }

}
<|endoftext|>