<commit_before>pub mod problems;
pub mod functions;
pub mod constraints;
pub mod solvers;

#[cfg(test)]
mod tests {
    use std::cell::RefCell;
    use math::variables::{AbstVar, new_var, new_const};
    use math::relationships::Relationship;
    use math::expressions::Expression;
    use objective::problems::ProblemType;
    use objective::functions::Function;
    use objective::constraints::Constraint;
    use objective::constraints::SystemOfConstraints;
    use objective::solvers::{transform_constraint_rels_to_eq, rearrange_fun_eq_zero};

    #[test]
    fn can_create_problem_types() {
        let p_t_max: ProblemType = ProblemType::MAX;
        assert_eq!(ProblemType::MAX, p_t_max);
        let p_t_min: ProblemType = ProblemType::MIN;
        assert_eq!(ProblemType::MIN, p_t_min);
    }

    #[test]
    fn can_create_functions() {
        let e1: Expression =
            Expression::new(vec![new_var("Z", 1.0)],
                            Relationship::EQ,
                            vec![new_var("x", 2.0), new_var("y", 3.0), new_const("bonus", 1000.0)]);
        let f1: Function = Function::new(e1, ProblemType::MAX);
        let exp1 = f1.exp().borrow();
        assert_eq!(ProblemType::MAX, *f1.p_type());
        assert_eq!("Z", exp1.lhs()[0].name());
        assert_eq!(1.0, exp1.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp1.rel());
        assert_eq!("x", exp1.rhs()[0].name());
        assert_eq!(2.0, exp1.rhs()[0].get_data());
        assert_eq!("y", exp1.rhs()[1].name());
        assert_eq!(3.0, exp1.rhs()[1].get_data());
        assert_eq!("bonus", exp1.rhs()[2].name());
        assert_eq!(1000.0, exp1.rhs()[2].get_data());

        let exp1_max = f1.exp_max().borrow();
        assert_eq!("Z", exp1_max.lhs()[0].name());
        assert_eq!(1.0, exp1_max.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp1_max.rel());
        assert_eq!("x", exp1_max.rhs()[0].name());
        assert_eq!(2.0, exp1_max.rhs()[0].get_data());
        assert_eq!("y", exp1_max.rhs()[1].name());
        assert_eq!(3.0, exp1_max.rhs()[1].get_data());
        assert_eq!("bonus", exp1_max.rhs()[2].name());
        assert_eq!(1000.0, exp1_max.rhs()[2].get_data());

        let e2: Expression =
            Expression::new(vec![new_var("Z", 1.0)],
                            Relationship::EQ,
                            vec![new_var("x", 2.0), new_var("y", 3.0), new_const("bonus", 1000.0)]);
        let f2: Function = Function::new(e2, ProblemType::MIN);
        let exp2 = f2.exp().borrow();
        assert_eq!(ProblemType::MIN, *f2.p_type());
        assert_eq!("Z", exp2.lhs()[0].name());
        assert_eq!(1.0, exp2.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp2.rel());
        assert_eq!("x", exp2.rhs()[0].name());
        assert_eq!(2.0, exp2.rhs()[0].get_data());
        assert_eq!("y", exp2.rhs()[1].name());
        assert_eq!(3.0, exp2.rhs()[1].get_data());
        assert_eq!("bonus", exp2.rhs()[2].name());
        assert_eq!(1000.0, exp2.rhs()[2].get_data());

        let exp2_max = f2.exp_max().borrow();
        assert_eq!("Q", exp2_max.lhs()[0].name());
        assert_eq!(1.0, exp2_max.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp2_max.rel());
        assert_eq!("x", exp2_max.rhs()[0].name());
        assert_eq!(-2.0, exp2_max.rhs()[0].get_data());
        assert_eq!("y", exp2_max.rhs()[1].name());
        assert_eq!(-3.0, exp2_max.rhs()[1].get_data());
        assert_eq!("bonus", exp2_max.rhs()[2].name());
        assert_eq!(-1000.0, exp2_max.rhs()[2].get_data());
    }

    #[test]
    fn can_create_constraints() {
        let e: Expression = Expression::new(vec![new_var("x", 2.0), new_var("y", 3.0)],
                                            Relationship::LEQ,
                                            vec![new_const("volume", 2300.0)]);
        let c1: Constraint = Constraint::Regular(RefCell::new(e));
        match c1 {
            Constraint::Regular(ref_cell) => {
                let exp = ref_cell.borrow();
                assert_eq!("x", exp.lhs()[0].name());
                assert_eq!(2.0, exp.lhs()[0].get_data());
                assert_eq!(Relationship::LEQ, *exp.rel());
                assert_eq!("y", exp.lhs()[1].name());
                assert_eq!(3.0, exp.lhs()[1].get_data());
                assert_eq!("volume", exp.rhs()[0].name());
                assert_eq!(2300.0, exp.rhs()[0].get_data());
            }
            _ => panic!("Unexpected variant."),
        }
        let c2: Constraint = Constraint::NonNegative(new_var("x", 2.0));
        match c2 {
            Constraint::NonNegative(abst_var) => {
                assert_eq!("x", abst_var.name());
                assert_eq!(2.0, abst_var.get_data());
            }
            _ => panic!("Unexpected variant."),
        }
    }

    #[test]
    fn can_create_system_of_constraints() {
        let e: Expression = Expression::new(vec![new_var("x", 2.0), new_var("y", 3.0)],
                                            Relationship::LEQ,
                                            vec![new_const("volume", 2300.0)]);
        let c1: Constraint = Constraint::Regular(RefCell::new(e));
        let c2: Constraint = Constraint::NonNegative(new_var("x", 2.0));
        let s: SystemOfConstraints = SystemOfConstraints::new(vec![c1, c2]);
        for constraint in s.system() {
            match constraint {
                &Constraint::Regular(ref ref_cell) => {
                    let exp = ref_cell.borrow();
                    assert_eq!("x", exp.lhs()[0].name());
                    assert_eq!(2.0, exp.lhs()[0].get_data());
                    assert_eq!(Relationship::LEQ, *exp.rel());
                    assert_eq!("y", exp.lhs()[1].name());
                    assert_eq!(3.0, exp.lhs()[1].get_data());
                    assert_eq!("volume", exp.rhs()[0].name());
                    assert_eq!(2300.0, exp.rhs()[0].get_data());
                }
                &Constraint::NonNegative(ref abst_var) => {
                    assert_eq!("x", abst_var.name());
                    assert_eq!(2.0, abst_var.get_data());
                }
            }
        }
    }

    #[test]
    fn can_transform_constraint_rels_to_eq() {
        let e1: Expression = Expression::new(vec![new_var("x", 2.0), new_var("y", 3.0)],
                                             Relationship::LEQ,
                                             vec![new_const("volume", 2300.0)]);
        let e2: Expression = Expression::new(vec![new_var("w", 6.0), new_var("z", 9.0)],
                                             Relationship::GEQ,
                                             vec![new_const("area", 300.0)]);
        let e3: Expression = Expression::new(vec![new_var("u", 61.0), new_var("t", 19.0)],
                                             Relationship::GEQ,
                                             vec![new_const("hyperplane", -3000.0)]);
        let c1: Constraint = Constraint::Regular(RefCell::new(e1));
        let c2: Constraint = Constraint::Regular(RefCell::new(e2));
        let c3: Constraint = Constraint::Regular(RefCell::new(e3));
        let c4: Constraint = Constraint::NonNegative(new_var("x", 2.0));
        let s: SystemOfConstraints = SystemOfConstraints::new(vec![c1, c2, c3, c4]);
        transform_constraint_rels_to_eq(&s);
        match s.system()[0] {
            Constraint::Regular(ref ref_cell) => {
                let exp = ref_cell.borrow();
                assert_eq!("x", exp.lhs()[0].name());
                assert_eq!(2.0, exp.lhs()[0].get_data());
                assert_eq!(Relationship::EQ, *exp.rel());
                assert_eq!("y", exp.lhs()[1].name());
                assert_eq!(3.0, exp.lhs()[1].get_data());
                assert_eq!(AbstVar::SlackVar { name: "sl1".to_string() }, exp.lhs()[2]);
                assert_eq!("volume", exp.rhs()[0].name());
                assert_eq!(2300.0, exp.rhs()[0].get_data());
            }
            _ => panic!("Unexpected variant in this program logic."),
        };
        match s.system()[1] {
            Constraint::Regular(ref ref_cell) => {
                let exp = ref_cell.borrow();
                assert_eq!("w", exp.lhs()[0].name());
                assert_eq!(6.0, exp.lhs()[0].get_data());
                assert_eq!(Relationship::EQ, *exp.rel());
                assert_eq!("z", exp.lhs()[1].name());
                assert_eq!(9.0, exp.lhs()[1].get_data());
                assert_eq!(AbstVar::SurplusVar { name: "su2".to_string() },
                           exp.lhs()[2]);
                assert_eq!("area", exp.rhs()[0].name());
                assert_eq!(300.0, exp.rhs()[0].get_data());
            }
            _ => panic!("Unexpected variant in this program logic."),
        };
        match s.system()[2] {
            Constraint::Regular(ref ref_cell) => {
                let exp = ref_cell.borrow();
                assert_eq!("u", exp.lhs()[0].name());
                assert_eq!(-61.0, exp.lhs()[0].get_data());
                assert_eq!(Relationship::EQ, *exp.rel());
                assert_eq!("t", exp.lhs()[1].name());
                assert_eq!(-19.0, exp.lhs()[1].get_data());
                assert_eq!(AbstVar::SlackVar { name: "sl3".to_string() }, exp.lhs()[2]);
                assert_eq!("hyperplane", exp.rhs()[0].name());
                assert_eq!(3000.0, exp.rhs()[0].get_data());
            }
            _ => panic!("Unexpected variant in this program logic."),
        };
        match s.system()[3] {
            Constraint::NonNegative(ref abst_var) => {
                assert_eq!("x", abst_var.name());
                assert_eq!(2.0, abst_var.get_data());
            }
            _ => panic!("Unexpected variant in this program logic."),
        };
    }

    #[test]
    fn can_rearrange_fun_eq_zero() {
        let e1: Expression =
            Expression::new(vec![new_var("Z", 1.0)],
                            Relationship::EQ,
                            vec![new_var("x", 2.0), new_var("y", 3.0), new_const("bonus", 1000.0)]);
        let mut f1: Function = Function::new(e1, ProblemType::MAX);
        rearrange_fun_eq_zero(&mut f1);
        let exp1 = f1.exp().borrow();
        assert_eq!(ProblemType::MAX, *f1.p_type());
        assert_eq!("zero", exp1.rhs()[0].name());
        assert_eq!(0.0, exp1.rhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp1.rel());
        assert_eq!("x", exp1.lhs()[0].name());
        assert_eq!(-2.0, exp1.lhs()[0].get_data());
        assert_eq!("y", exp1.lhs()[1].name());
        assert_eq!(-3.0, exp1.lhs()[1].get_data());
        assert_eq!("bonus", exp1.lhs()[2].name());
        assert_eq!(-1000.0, exp1.lhs()[2].get_data());
        assert_eq!("Z", exp1.lhs()[3].name());
        assert_eq!(1.0, exp1.lhs()[3].get_data());

        let exp1_max = f1.exp_max().borrow();
        assert_eq!("zero", exp1_max.rhs()[0].name());
        assert_eq!(0.0, exp1_max.rhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp1_max.rel());
        assert_eq!("x", exp1_max.lhs()[0].name());
        assert_eq!(-2.0, exp1_max.lhs()[0].get_data());
        assert_eq!("y", exp1_max.lhs()[1].name());
        assert_eq!(-3.0, exp1_max.lhs()[1].get_data());
        assert_eq!("bonus", exp1_max.lhs()[2].name());
        assert_eq!(-1000.0, exp1_max.lhs()[2].get_data());
        assert_eq!("Z", exp1_max.lhs()[3].name());
        assert_eq!(1.0, exp1_max.lhs()[3].get_data());

        let e2: Expression =
            Expression::new(vec![new_var("Z", 1.0)],
                            Relationship::EQ,
                            vec![new_var("x", 2.0), new_var("y", 3.0), new_const("bonus", 1000.0)]);
        let mut f2: Function = Function::new(e2, ProblemType::MIN);
        rearrange_fun_eq_zero(&mut f2);
        let exp2 = f2.exp().borrow();
        assert_eq!(ProblemType::MIN, *f2.p_type());
        assert_eq!("Z", exp2.lhs()[0].name());
        assert_eq!(1.0, exp2.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp2.rel());
        assert_eq!("x", exp2.rhs()[0].name());
        assert_eq!(2.0, exp2.rhs()[0].get_data());
        assert_eq!("y", exp2.rhs()[1].name());
        assert_eq!(3.0, exp2.rhs()[1].get_data());
        assert_eq!("bonus", exp2.rhs()[2].name());
        assert_eq!(1000.0, exp2.rhs()[2].get_data());

        let exp2_max = f2.exp_max().borrow();
        assert_eq!("zero", exp2_max.rhs()[0].name());
        assert_eq!(0.0, exp2_max.rhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp2_max.rel());
        assert_eq!("x", exp2_max.lhs()[0].name());
        assert_eq!(2.0, exp2_max.lhs()[0].get_data());
        assert_eq!("y", exp2_max.lhs()[1].name());
        assert_eq!(3.0, exp2_max.lhs()[1].get_data());
        assert_eq!("bonus", exp2_max.lhs()[2].name());
        assert_eq!(1000.0, exp2_max.lhs()[2].get_data());
        assert_eq!("Q", exp2_max.lhs()[3].name());
        assert_eq!(1.0, exp2_max.lhs()[3].get_data());
    }
}
<commit_msg>Refactor objective module tests.<commit_after>pub mod problems;
pub mod functions;
pub mod constraints;
pub mod solvers;

#[cfg(test)]
mod tests {
    use std::cell::RefCell;
    use math::variables::{AbstVar, new_var, new_const};
    use math::relationships::Relationship;
    use math::expressions::Expression;
    use objective::problems::ProblemType;
    use objective::functions::Function;
    use objective::constraints::Constraint;
    use objective::constraints::SystemOfConstraints;
    use objective::solvers::{transform_constraint_rels_to_eq, rearrange_fun_eq_zero};

    #[test]
    fn can_create_problem_types() {
        let p_t_max = ProblemType::MAX;
        assert_eq!(ProblemType::MAX, p_t_max);
        let p_t_min = ProblemType::MIN;
        assert_eq!(ProblemType::MIN, p_t_min);
    }

    #[test]
    fn can_create_functions() {
        let e1 =
            Expression::new(vec![new_var("Z", 1.0)],
                            Relationship::EQ,
                            vec![new_var("x", 2.0), new_var("y", 3.0), new_const("bonus", 1000.0)]);
        let f1 = Function::new(e1, ProblemType::MAX);
        let exp1 = f1.exp().borrow();
        assert_eq!(ProblemType::MAX, *f1.p_type());
        assert_eq!("Z", exp1.lhs()[0].name());
        assert_eq!(1.0, exp1.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp1.rel());
        assert_eq!("x", exp1.rhs()[0].name());
        assert_eq!(2.0, exp1.rhs()[0].get_data());
        assert_eq!("y", exp1.rhs()[1].name());
        assert_eq!(3.0, exp1.rhs()[1].get_data());
        assert_eq!("bonus", exp1.rhs()[2].name());
        assert_eq!(1000.0, exp1.rhs()[2].get_data());

        let exp1_max = f1.exp_max().borrow();
        assert_eq!("Z", exp1_max.lhs()[0].name());
        assert_eq!(1.0, exp1_max.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp1_max.rel());
        assert_eq!("x", exp1_max.rhs()[0].name());
        assert_eq!(2.0, exp1_max.rhs()[0].get_data());
        assert_eq!("y", exp1_max.rhs()[1].name());
        assert_eq!(3.0, exp1_max.rhs()[1].get_data());
        assert_eq!("bonus", exp1_max.rhs()[2].name());
        assert_eq!(1000.0, exp1_max.rhs()[2].get_data());

        let e2 =
            Expression::new(vec![new_var("Z", 1.0)],
                            Relationship::EQ,
                            vec![new_var("x", 2.0), new_var("y", 3.0), new_const("bonus", 1000.0)]);
        let f2 = Function::new(e2, ProblemType::MIN);
        let exp2 = f2.exp().borrow();
        assert_eq!(ProblemType::MIN, *f2.p_type());
        assert_eq!("Z", exp2.lhs()[0].name());
        assert_eq!(1.0, exp2.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp2.rel());
        assert_eq!("x", exp2.rhs()[0].name());
        assert_eq!(2.0, exp2.rhs()[0].get_data());
        assert_eq!("y", exp2.rhs()[1].name());
        assert_eq!(3.0, exp2.rhs()[1].get_data());
        assert_eq!("bonus", exp2.rhs()[2].name());
        assert_eq!(1000.0, exp2.rhs()[2].get_data());

        let exp2_max = f2.exp_max().borrow();
        assert_eq!("Q", exp2_max.lhs()[0].name());
        assert_eq!(1.0, exp2_max.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp2_max.rel());
        assert_eq!("x", exp2_max.rhs()[0].name());
        assert_eq!(-2.0, exp2_max.rhs()[0].get_data());
        assert_eq!("y", exp2_max.rhs()[1].name());
        assert_eq!(-3.0, exp2_max.rhs()[1].get_data());
        assert_eq!("bonus", exp2_max.rhs()[2].name());
        assert_eq!(-1000.0, exp2_max.rhs()[2].get_data());
    }

    #[test]
    fn can_create_constraints() {
        let exp = Expression::new(vec![new_var("x", 2.0), new_var("y", 3.0)],
                                  Relationship::LEQ,
                                  vec![new_const("volume", 2300.0)]);
        let c1 = Constraint::Regular(RefCell::new(exp));
        match c1 {
            Constraint::Regular(ref_cell) => {
                let exp = ref_cell.borrow();
                assert_eq!("x", exp.lhs()[0].name());
                assert_eq!(2.0, exp.lhs()[0].get_data());
                assert_eq!(Relationship::LEQ, *exp.rel());
                assert_eq!("y", exp.lhs()[1].name());
                assert_eq!(3.0, exp.lhs()[1].get_data());
                assert_eq!("volume", exp.rhs()[0].name());
                assert_eq!(2300.0, exp.rhs()[0].get_data());
            }
            _ => panic!("Unexpected variant."),
        }

        let c2 = Constraint::NonNegative(new_var("x", 2.0));
        match c2 {
            Constraint::NonNegative(abst_var) => {
                assert_eq!("x", abst_var.name());
                assert_eq!(2.0, abst_var.get_data());
            }
            _ => panic!("Unexpected variant."),
        }
    }

    #[test]
    fn can_create_system_of_constraints() {
        let exp = Expression::new(vec![new_var("x", 2.0), new_var("y", 3.0)],
                                  Relationship::LEQ,
                                  vec![new_const("volume", 2300.0)]);
        let c1 = Constraint::Regular(RefCell::new(exp));
        let c2 = Constraint::NonNegative(new_var("x", 2.0));
        let s = SystemOfConstraints::new(vec![c1, c2]);
        for constraint in s.system() {
            match constraint {
                &Constraint::Regular(ref ref_cell) => {
                    let exp = ref_cell.borrow();
                    assert_eq!("x", exp.lhs()[0].name());
                    assert_eq!(2.0, exp.lhs()[0].get_data());
                    assert_eq!(Relationship::LEQ, *exp.rel());
                    assert_eq!("y", exp.lhs()[1].name());
                    assert_eq!(3.0, exp.lhs()[1].get_data());
                    assert_eq!("volume", exp.rhs()[0].name());
                    assert_eq!(2300.0, exp.rhs()[0].get_data());
                }
                &Constraint::NonNegative(ref abst_var) => {
                    assert_eq!("x", abst_var.name());
                    assert_eq!(2.0, abst_var.get_data());
                }
            }
        }
    }

    #[test]
    fn can_transform_constraint_rels_to_eq() {
        let exp1 = Expression::new(vec![new_var("x", 2.0), new_var("y", 3.0)],
                                   Relationship::LEQ,
                                   vec![new_const("volume", 2300.0)]);
        let exp2 = Expression::new(vec![new_var("w", 6.0), new_var("z", 9.0)],
                                   Relationship::GEQ,
                                   vec![new_const("area", 300.0)]);
        let exp3 = Expression::new(vec![new_var("u", 61.0), new_var("t", 19.0)],
                                   Relationship::GEQ,
                                   vec![new_const("hyperplane", -3000.0)]);
        let c1 = Constraint::Regular(RefCell::new(exp1));
        let c2 = Constraint::Regular(RefCell::new(exp2));
        let c3 = Constraint::Regular(RefCell::new(exp3));
        let c4 = Constraint::NonNegative(new_var("x", 2.0));
        let s = SystemOfConstraints::new(vec![c1, c2, c3, c4]);
        transform_constraint_rels_to_eq(&s);
        match s.system()[0] {
            Constraint::Regular(ref ref_cell) => {
                let exp = ref_cell.borrow();
                assert_eq!("x", exp.lhs()[0].name());
                assert_eq!(2.0, exp.lhs()[0].get_data());
                assert_eq!(Relationship::EQ, *exp.rel());
                assert_eq!("y", exp.lhs()[1].name());
                assert_eq!(3.0, exp.lhs()[1].get_data());
                assert_eq!(AbstVar::SlackVar { name: "sl1".to_string() }, exp.lhs()[2]);
                assert_eq!("volume", exp.rhs()[0].name());
                assert_eq!(2300.0, exp.rhs()[0].get_data());
            }
            _ => panic!("Unexpected variant in this program logic."),
        };
        match s.system()[1] {
            Constraint::Regular(ref ref_cell) => {
                let exp = ref_cell.borrow();
                assert_eq!("w", exp.lhs()[0].name());
                assert_eq!(6.0, exp.lhs()[0].get_data());
                assert_eq!(Relationship::EQ, *exp.rel());
                assert_eq!("z", exp.lhs()[1].name());
                assert_eq!(9.0, exp.lhs()[1].get_data());
                assert_eq!(AbstVar::SurplusVar { name: "su2".to_string() },
                           exp.lhs()[2]);
                assert_eq!("area", exp.rhs()[0].name());
                assert_eq!(300.0, exp.rhs()[0].get_data());
            }
            _ => panic!("Unexpected variant in this program logic."),
        };
        match s.system()[2] {
            Constraint::Regular(ref ref_cell) => {
                let exp = ref_cell.borrow();
                assert_eq!("u", exp.lhs()[0].name());
                assert_eq!(-61.0, exp.lhs()[0].get_data());
                assert_eq!(Relationship::EQ, *exp.rel());
                assert_eq!("t", exp.lhs()[1].name());
                assert_eq!(-19.0, exp.lhs()[1].get_data());
                assert_eq!(AbstVar::SlackVar { name: "sl3".to_string() }, exp.lhs()[2]);
                assert_eq!("hyperplane", exp.rhs()[0].name());
                assert_eq!(3000.0, exp.rhs()[0].get_data());
            }
            _ => panic!("Unexpected variant in this program logic."),
        };
        match s.system()[3] {
            Constraint::NonNegative(ref abst_var) => {
                assert_eq!("x", abst_var.name());
                assert_eq!(2.0, abst_var.get_data());
            }
            _ => panic!("Unexpected variant in this program logic."),
        };
    }

    #[test]
    fn can_rearrange_fun_eq_zero() {
        let e1 =
            Expression::new(vec![new_var("Z", 1.0)],
                            Relationship::EQ,
                            vec![new_var("x", 2.0), new_var("y", 3.0), new_const("bonus", 1000.0)]);
        let mut f1 = Function::new(e1, ProblemType::MAX);
        rearrange_fun_eq_zero(&mut f1);
        let exp1 = f1.exp().borrow();
        assert_eq!(ProblemType::MAX, *f1.p_type());
        assert_eq!("zero", exp1.rhs()[0].name());
        assert_eq!(0.0, exp1.rhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp1.rel());
        assert_eq!("x", exp1.lhs()[0].name());
        assert_eq!(-2.0, exp1.lhs()[0].get_data());
        assert_eq!("y", exp1.lhs()[1].name());
        assert_eq!(-3.0, exp1.lhs()[1].get_data());
        assert_eq!("bonus", exp1.lhs()[2].name());
        assert_eq!(-1000.0, exp1.lhs()[2].get_data());
        assert_eq!("Z", exp1.lhs()[3].name());
        assert_eq!(1.0, exp1.lhs()[3].get_data());

        let exp1_max = f1.exp_max().borrow();
        assert_eq!("zero", exp1_max.rhs()[0].name());
        assert_eq!(0.0, exp1_max.rhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp1_max.rel());
        assert_eq!("x", exp1_max.lhs()[0].name());
        assert_eq!(-2.0, exp1_max.lhs()[0].get_data());
        assert_eq!("y", exp1_max.lhs()[1].name());
        assert_eq!(-3.0, exp1_max.lhs()[1].get_data());
        assert_eq!("bonus", exp1_max.lhs()[2].name());
        assert_eq!(-1000.0, exp1_max.lhs()[2].get_data());
        assert_eq!("Z", exp1_max.lhs()[3].name());
        assert_eq!(1.0, exp1_max.lhs()[3].get_data());

        let e2 =
            Expression::new(vec![new_var("Z", 1.0)],
                            Relationship::EQ,
                            vec![new_var("x", 2.0), new_var("y", 3.0), new_const("bonus", 1000.0)]);
        let mut f2 = Function::new(e2, ProblemType::MIN);
        rearrange_fun_eq_zero(&mut f2);
        let exp2 = f2.exp().borrow();
        assert_eq!(ProblemType::MIN, *f2.p_type());
        assert_eq!("Z", exp2.lhs()[0].name());
        assert_eq!(1.0, exp2.lhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp2.rel());
        assert_eq!("x", exp2.rhs()[0].name());
        assert_eq!(2.0, exp2.rhs()[0].get_data());
        assert_eq!("y", exp2.rhs()[1].name());
        assert_eq!(3.0, exp2.rhs()[1].get_data());
        assert_eq!("bonus", exp2.rhs()[2].name());
        assert_eq!(1000.0, exp2.rhs()[2].get_data());

        let exp2_max = f2.exp_max().borrow();
        assert_eq!("zero", exp2_max.rhs()[0].name());
        assert_eq!(0.0, exp2_max.rhs()[0].get_data());
        assert_eq!(Relationship::EQ, *exp2_max.rel());
        assert_eq!("x", exp2_max.lhs()[0].name());
        assert_eq!(2.0, exp2_max.lhs()[0].get_data());
        assert_eq!("y", exp2_max.lhs()[1].name());
        assert_eq!(3.0, exp2_max.lhs()[1].get_data());
        assert_eq!("bonus", exp2_max.lhs()[2].name());
        assert_eq!(1000.0, exp2_max.lhs()[2].get_data());
        assert_eq!("Q", exp2_max.lhs()[3].name());
        assert_eq!(1.0, exp2_max.lhs()[3].get_data());
    }
}
<|endoftext|>


<commit_before>// Copyright (c) 2016
// Jeff Nettleton
//
// Licensed under the MIT license (http://opensource.org/licenses/MIT). This
// file may not be copied, modified, or distributed except according to those
// terms

use std;
use std::collections::HashMap;
use rustc_serialize::{json, Decodable};

/// This enum represents the various types of HTTP requests.
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
pub enum Method {
    Get,
    Put,
    Post,
    Delete,
    NoImpl,
}

/// This enum represents the errors that might be encountered.
#[derive(Debug)]
pub enum RequestError {
    JsonObjError(json::BuilderError),
    JsonStrError(json::DecoderError),
    StrCopyError(std::string::FromUtf8Error),
}

impl From<json::BuilderError> for RequestError {
    fn from(err: json::BuilderError) -> RequestError {
        RequestError::JsonObjError(err)
    }
}

impl From<json::DecoderError> for RequestError {
    fn from(err: json::DecoderError) -> RequestError {
        RequestError::JsonStrError(err)
    }
}

impl From<std::string::FromUtf8Error> for RequestError {
    fn from(err: std::string::FromUtf8Error) -> RequestError {
        RequestError::StrCopyError(err)
    }
}

/// A trait that allows for extracting variables from URIs.
pub trait FromUri {
    /// A function to parse a string into the correct type.
    fn from_uri(data: &str) -> Self;
}

impl FromUri for String {
    fn from_uri(data: &str) -> String {
        String::from(data)
    }
}

impl FromUri for i32 {
    fn from_uri(data: &str) -> i32 {
        match data.parse::<i32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched integer can't be parsed: {:?}", e),
        }
    }
}

impl FromUri for u32 {
    fn from_uri(data: &str) -> u32 {
        match data.parse::<u32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched integer can't be parsed: {:?}", e),
        }
    }
}

impl FromUri for f32 {
    fn from_uri(data: &str) -> f32 {
        match data.parse::<f32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched float can't be parsed: {:?}", e),
        }
    }
}

/// This struct represents a request from an HTTP client.
#[derive(Debug)]
pub struct Request {
    pub method:  Method,
    pub path:    String,
    pub payload: Vec<u8>,
    pub params:  HashMap<String, String>,
    headers:     HashMap<String, String>,
}

impl Request {
    /// Create a new, empty Request.
    pub fn new() -> Request {
        Request {
            method:  Method::NoImpl,
            path:    String::new(),
            headers: HashMap::new(),
            params:  HashMap::new(),
            payload: Vec::with_capacity(2048),
        }
    }

    /// Create a Request from an HTTP request string.
    pub fn from_str(rqstr: &str) -> Request {
        let mut req = Request::new();
        req.parse(rqstr);
        req
    }

    /// Get an HTTP header contained in the Request.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let browser = req.get_header("User-Agent");
    ///
    ///     match browser {
    ///         Some(ua) => utils::make_response(format!("You're using {}!", ua), "text/plain", 200),
    ///         None     => utils::make_response("Bad browser, no user agent!", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_header(&self, name: &str) -> Option<String> {
        let key = String::from(name);

        match self.headers.get(&key) {
            Some(val)   => Some(val.clone()),
            None        => None,
        }
    }

    /// Get a variable from the URI.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the route "/hello/<str:name>"
    /// fn handler(req: &Request) -> Response {
    ///     let name: String = req.get("name");
    ///     utils::make_response(format!("<b>Hello, {}!</b>", name), "text/html", 200)
    /// }
    /// ```
    pub fn get<T: FromUri>(&self, name: &str) -> T {
        if !self.params.contains_key(name) {
            panic!("invalid route parameter {:?}", name);
        }

        FromUri::from_uri(&self.params[name])
    }

    /// Get a raw JSON payload from the request.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the POST route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let data = req.get_json();
    ///
    ///     match data {
    ///         Ok(val) => utils::make_response(format!("We got: {}", val), "text/plain", 200),
    ///         Err(_)  => utils::make_response("We got nothing :(", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_json(&self) -> Result<json::Json, RequestError> {
        let payload = String::from_utf8(self.payload.clone())?;
        let data = json::Json::from_str(&payload)?;

        Ok(data)
    }

    /// Get a composed JSON payload from the request.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// use canteen::{Request, Response};
    ///
    /// #[derive(RustcDecodable)]
    /// struct Foo {
    ///     item: i32,
    /// }
    ///
    /// // Given the POST route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let data: Foo = req.get_json_obj();
    ///
    ///     match data {
    ///         Ok(foo) => utils::make_response(format!("We got: {}!", data.item), "text/plain", 200),
    ///         Err(_)  => utils::make_response("We got nothing :(", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_json_obj<T: Decodable>(&self) -> Result<T, RequestError> {
        let payload = String::from_utf8(self.payload.clone())?;
        let data = json::decode(&payload)?;

        Ok(data)
    }

    fn parse(&mut self, rqstr: &str) {
        let mut buf: Vec<&str> = rqstr.splitn(2, "\r\n").collect();
        let ask: Vec<&str> = buf[0].splitn(3, ' ').collect();

        self.method = match ask[0] {
            "GET"           => Method::Get,
            "PUT" | "PATCH" => Method::Put,
            "POST"          => Method::Post,
            "DELETE"        => Method::Delete,
            _               => Method::NoImpl,
        };
        self.path = String::from(ask[1]);

        loop {
            buf = buf[1].splitn(2, "\r\n").collect();

            if buf[0] == "" {
                if buf.len() == 1 || buf[1] == "" {
                    // no payload
                    break;
                }

                self.payload.extend(buf[1].as_bytes());
                break;
            }

            let hdr: Vec<&str> = buf[0].splitn(2, ": ").collect();

            if hdr.len() == 2 {
                self.headers.insert(String::from(hdr[0]), String::from(hdr[1]));
            }
        }
    }
}

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

    #[derive(RustcDecodable)]
    struct Foo {
        item: i32,
    }

    #[test]
    fn test_fromuri_trait_i32() {
        let pos = String::from("1234");
        assert_eq!(1234, FromUri::from_uri(&pos));

        let neg = String::from("-4321");
        assert_eq!(-4321, FromUri::from_uri(&neg));
    }

    #[test]
    fn test_fromuri_trait_u32() {
        let orig = String::from("1234");
        assert_eq!(1234, FromUri::from_uri(&orig));
    }

    #[test]
    fn test_fromuri_trait_string() {
        let orig = String::from("foobar");
        assert_eq!("foobar", <String as FromUri>::from_uri(&orig));
    }

    #[test]
    fn test_fromuri_trait_float() {
        let pos = String::from("123.45");
        assert_eq!(123.45f32, FromUri::from_uri(&pos));

        let neg = String::from("-54.321");
        assert_eq!(-54.321f32, FromUri::from_uri(&neg));
    }

    #[test]
    fn test_get_fromuri_i32() {
        let mut req = Request::new();
        req.params.insert(String::from("test"), String::from("1234"));

        assert_eq!(1234, req.get("test"));
    }

    #[test]
    fn test_get_json() {
        let mut req = Request::new();
        req.payload.extend_from_slice("{ \"item\": 123 }".as_bytes());

        let data = req.get_json().unwrap();

        assert_eq!(true, data.is_object());

        let obj = data.as_object().unwrap();
        let val = obj.get("item").unwrap();

        assert_eq!(true, val.is_u64());
        assert_eq!(123u64, val.as_u64().unwrap());
    }

    #[test]
    fn test_get_json_obj() {
        let mut req = Request::new();
        req.payload.extend_from_slice("{ \"item\": 123 }".as_bytes());

        let data: Foo = req.get_json_obj().unwrap();

        assert_eq!(123, data.item);
    }
}
<commit_msg>use the try macro for now so that tests pass<commit_after>// Copyright (c) 2016
// Jeff Nettleton
//
// Licensed under the MIT license (http://opensource.org/licenses/MIT). This
// file may not be copied, modified, or distributed except according to those
// terms

use std;
use std::collections::HashMap;
use rustc_serialize::{json, Decodable};

/// This enum represents the various types of HTTP requests.
#[derive(PartialEq, Eq, Hash, Debug, Copy, Clone)]
pub enum Method {
    Get,
    Put,
    Post,
    Delete,
    NoImpl,
}

/// This enum represents the errors that might be encountered.
#[derive(Debug)]
pub enum RequestError {
    JsonObjError(json::BuilderError),
    JsonStrError(json::DecoderError),
    StrCopyError(std::string::FromUtf8Error),
}

impl From<json::BuilderError> for RequestError {
    fn from(err: json::BuilderError) -> RequestError {
        RequestError::JsonObjError(err)
    }
}

impl From<json::DecoderError> for RequestError {
    fn from(err: json::DecoderError) -> RequestError {
        RequestError::JsonStrError(err)
    }
}

impl From<std::string::FromUtf8Error> for RequestError {
    fn from(err: std::string::FromUtf8Error) -> RequestError {
        RequestError::StrCopyError(err)
    }
}

/// A trait that allows for extracting variables from URIs.
pub trait FromUri {
    /// A function to parse a string into the correct type.
    fn from_uri(data: &str) -> Self;
}

impl FromUri for String {
    fn from_uri(data: &str) -> String {
        String::from(data)
    }
}

impl FromUri for i32 {
    fn from_uri(data: &str) -> i32 {
        match data.parse::<i32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched integer can't be parsed: {:?}", e),
        }
    }
}

impl FromUri for u32 {
    fn from_uri(data: &str) -> u32 {
        match data.parse::<u32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched integer can't be parsed: {:?}", e),
        }
    }
}

impl FromUri for f32 {
    fn from_uri(data: &str) -> f32 {
        match data.parse::<f32>() {
            Ok(v)  => v,
            Err(e) => panic!("matched float can't be parsed: {:?}", e),
        }
    }
}

/// This struct represents a request from an HTTP client.
#[derive(Debug)]
pub struct Request {
    pub method:  Method,
    pub path:    String,
    pub payload: Vec<u8>,
    pub params:  HashMap<String, String>,
    headers:     HashMap<String, String>,
}

impl Request {
    /// Create a new, empty Request.
    pub fn new() -> Request {
        Request {
            method:  Method::NoImpl,
            path:    String::new(),
            headers: HashMap::new(),
            params:  HashMap::new(),
            payload: Vec::with_capacity(2048),
        }
    }

    /// Create a Request from an HTTP request string.
    pub fn from_str(rqstr: &str) -> Request {
        let mut req = Request::new();
        req.parse(rqstr);
        req
    }

    /// Get an HTTP header contained in the Request.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let browser = req.get_header("User-Agent");
    ///
    ///     match browser {
    ///         Some(ua) => utils::make_response(format!("You're using {}!", ua), "text/plain", 200),
    ///         None     => utils::make_response("Bad browser, no user agent!", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_header(&self, name: &str) -> Option<String> {
        let key = String::from(name);

        match self.headers.get(&key) {
            Some(val)   => Some(val.clone()),
            None        => None,
        }
    }

    /// Get a variable from the URI.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the route "/hello/<str:name>"
    /// fn handler(req: &Request) -> Response {
    ///     let name: String = req.get("name");
    ///     utils::make_response(format!("<b>Hello, {}!</b>", name), "text/html", 200)
    /// }
    /// ```
    pub fn get<T: FromUri>(&self, name: &str) -> T {
        if !self.params.contains_key(name) {
            panic!("invalid route parameter {:?}", name);
        }

        FromUri::from_uri(&self.params[name])
    }

    /// Get a raw JSON payload from the request.
    ///
    /// # Examples
    ///
    /// ```rust
    /// use canteen::{Request, Response};
    /// use canteen::utils;
    ///
    /// // Given the POST route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let data = req.get_json();
    ///
    ///     match data {
    ///         Ok(val) => utils::make_response(format!("We got: {}", val), "text/plain", 200),
    ///         Err(_)  => utils::make_response("We got nothing :(", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_json(&self) -> Result<json::Json, RequestError> {
        let payload = try!(String::from_utf8(self.payload.clone()));
        let data = try!(json::Json::from_str(&payload));

        Ok(data)
    }

    /// Get a composed JSON payload from the request.
    ///
    /// # Examples
    ///
    /// ```rust,ignore
    /// use canteen::{Request, Response};
    ///
    /// #[derive(RustcDecodable)]
    /// struct Foo {
    ///     item: i32,
    /// }
    ///
    /// // Given the POST route "/hello"
    /// fn handler(req: &Request) -> Response {
    ///     let data: Foo = req.get_json_obj();
    ///
    ///     match data {
    ///         Ok(foo) => utils::make_response(format!("We got: {}!", data.item), "text/plain", 200),
    ///         Err(_)  => utils::make_response("We got nothing :(", "text/plain", 200),
    ///     }
    /// }
    /// ```
    pub fn get_json_obj<T: Decodable>(&self) -> Result<T, RequestError> {
        let payload = try!(String::from_utf8(self.payload.clone()));
        let data = try!(json::decode(&payload));

        Ok(data)
    }

    fn parse(&mut self, rqstr: &str) {
        let mut buf: Vec<&str> = rqstr.splitn(2, "\r\n").collect();
        let ask: Vec<&str> = buf[0].splitn(3, ' ').collect();

        self.method = match ask[0] {
            "GET"           => Method::Get,
            "PUT" | "PATCH" => Method::Put,
            "POST"          => Method::Post,
            "DELETE"        => Method::Delete,
            _               => Method::NoImpl,
        };
        self.path = String::from(ask[1]);

        loop {
            buf = buf[1].splitn(2, "\r\n").collect();

            if buf[0] == "" {
                if buf.len() == 1 || buf[1] == "" {
                    // no payload
                    break;
                }

                self.payload.extend(buf[1].as_bytes());
                break;
            }

            let hdr: Vec<&str> = buf[0].splitn(2, ": ").collect();

            if hdr.len() == 2 {
                self.headers.insert(String::from(hdr[0]), String::from(hdr[1]));
            }
        }
    }
}

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

    #[derive(RustcDecodable)]
    struct Foo {
        item: i32,
    }

    #[test]
    fn test_fromuri_trait_i32() {
        let pos = String::from("1234");
        assert_eq!(1234, FromUri::from_uri(&pos));

        let neg = String::from("-4321");
        assert_eq!(-4321, FromUri::from_uri(&neg));
    }

    #[test]
    fn test_fromuri_trait_u32() {
        let orig = String::from("1234");
        assert_eq!(1234, FromUri::from_uri(&orig));
    }

    #[test]
    fn test_fromuri_trait_string() {
        let orig = String::from("foobar");
        assert_eq!("foobar", <String as FromUri>::from_uri(&orig));
    }

    #[test]
    fn test_fromuri_trait_float() {
        let pos = String::from("123.45");
        assert_eq!(123.45f32, FromUri::from_uri(&pos));

        let neg = String::from("-54.321");
        assert_eq!(-54.321f32, FromUri::from_uri(&neg));
    }

    #[test]
    fn test_get_fromuri_i32() {
        let mut req = Request::new();
        req.params.insert(String::from("test"), String::from("1234"));

        assert_eq!(1234, req.get("test"));
    }

    #[test]
    fn test_get_json() {
        let mut req = Request::new();
        req.payload.extend_from_slice("{ \"item\": 123 }".as_bytes());

        let data = req.get_json().unwrap();

        assert_eq!(true, data.is_object());

        let obj = data.as_object().unwrap();
        let val = obj.get("item").unwrap();

        assert_eq!(true, val.is_u64());
        assert_eq!(123u64, val.as_u64().unwrap());
    }

    #[test]
    fn test_get_json_obj() {
        let mut req = Request::new();
        req.payload.extend_from_slice("{ \"item\": 123 }".as_bytes());

        let data: Foo = req.get_json_obj().unwrap();

        assert_eq!(123, data.item);
    }
}
<|endoftext|>


<commit_before>//! common functions for all parsers

use crate::Variant;

/// Interface of all Rusticata parsers.
///
/// A object implementing the RParser trait is an instance of a parser,
/// including the state (and all associated variables).
pub trait RParser : Send + Sync {
    // XXX static functions seem to cause problems with hashmaps
    // fn probe(&[u8]) -> bool;

    /// Parsing function
    ///
    /// This function is called for every packet of a connection.
    ///
    /// Arguments:
    ///
    /// - `self`: the state (parser instance)
    /// - a slice on the packet data
    /// - the direction of this packet (0: to server, 1: to client)
    ///
    /// Return value:
    ///
    /// `R_STATUS_OK` or `R_STATUS_FAIL`, possibly or'ed with
    /// `R_STATUS_EVENTS` if parsing events were raised.
    fn parse(&mut self, data:&[u8], direction:u8) -> u32;

    fn get(&self, _key: &str) -> Option<Variant> { None }
}

/// Interface of a parser builder
pub trait RBuilder : Send + Sync {
    fn new(&self) -> Box<dyn RParser>;

    fn probe(&self, data:&[u8]) -> bool;
}

// status: return code, events

pub const R_STATUS_EVENTS : u32  = 0x0100;

pub const R_STATUS_OK : u32      = 0x0000;
pub const R_STATUS_FAIL : u32    = 0x0001;

pub const R_STATUS_EV_MASK : u32 = 0x0f00;
pub const R_STATUS_MASK : u32    = 0x00ff;

#[macro_export]
macro_rules! r_status_is_ok {
    ($status:expr) => { ($status & $crate::R_STATUS_MASK) == $crate::R_STATUS_MASK }
}

#[macro_export]
macro_rules! r_status_has_events {
    ($status:expr) => { ($status & $crate::R_STATUS_EV_MASK) == $crate::R_STATUS_EVENTS }
}

// Constants
pub const STREAM_TOSERVER : u8 = 0;
pub const STREAM_TOCLIENT : u8 = 1;

// Helper macros
// We can't define them (can we ?) in the trait, partly because of the 'no_mangle' and 'extern'
// stuff.
// This forces to use macros in addition to the trait, but at least provides a proper way of
// encapsulating the translation of C variables to rust.

#[macro_export]
macro_rules! r_declare_state_new {
    ($f:ident, $ty:ident, $args:expr) => {
        #[no_mangle]
        pub extern "C" fn $f() -> *mut libc::c_char {
            let b = Box::new($ty::new($args));
            unsafe { mem::transmute(b) }
        }
    }
}

#[macro_export]
macro_rules! r_declare_state_free {
    ($f:ident, $ty:ident, $expr:expr) => {
        impl<'a> Drop for $ty<'a> {
            fn drop(&mut self) {
                $expr
            }
        }

        #[no_mangle]
        pub unsafe extern fn $f(ptr: *mut libc::c_char)
        {
            let b: Box<$ty> = mem::transmute(ptr);
            // reference will be dropped automatically, and allocated memory
            // will be freed
            // but let's do it explicitly
            drop(b);
        }
    }
}

#[macro_export]
macro_rules! r_implement_probe {
    ($f:ident, $g:ident) => {
        #[no_mangle]
        pub extern "C" fn $f(input: *const c_char, input_len: u32, _offset: *const c_char) -> u32 {
            let data_len = input_len as usize;
            let data : &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, data_len) };
            let res = ::std::panic::catch_unwind(|| {
                match $g(data) {
                    true  => 1,
                    false => 0,
                }
            });
            match res {
                Ok(v)  => v,
                Err(_) => 0xffffffff,
            }
        }
    }
}

#[macro_export]
macro_rules! r_implement_parse {
    ($f:ident, $g:ident) => {
        #[no_mangle]
        pub extern "C" fn $f(direction: u8, input: *const c_char, input_len: u32, ptr: *mut $g) -> u32 {
            let data_len = input_len as usize;
            let data : &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, data_len) };
            if ptr.is_null() { return 0xffffffff; };
            let res = ::std::panic::catch_unwind(|| {
                let parser = unsafe { &mut *ptr };
                parser.parse(data, direction)
            });
            match res {
                Ok(v)  => v,
                Err(_) => 0xffffffff,
            }
        }
    }
}

<commit_msg>RParser: add keys() function to list available keys<commit_after>//! common functions for all parsers

use crate::Variant;

/// Interface of all Rusticata parsers.
///
/// A object implementing the RParser trait is an instance of a parser,
/// including the state (and all associated variables).
pub trait RParser : Send + Sync {
    // XXX static functions seem to cause problems with hashmaps
    // fn probe(&[u8]) -> bool;

    /// Parsing function
    ///
    /// This function is called for every packet of a connection.
    ///
    /// Arguments:
    ///
    /// - `self`: the state (parser instance)
    /// - a slice on the packet data
    /// - the direction of this packet (0: to server, 1: to client)
    ///
    /// Return value:
    ///
    /// `R_STATUS_OK` or `R_STATUS_FAIL`, possibly or'ed with
    /// `R_STATUS_EVENTS` if parsing events were raised.
    fn parse(&mut self, data:&[u8], direction:u8) -> u32;

    /// Request data from key
    fn get(&self, _key: &str) -> Option<Variant> { None }

    /// Returns the available keys for the `get` function
    fn keys(&self) -> ::std::slice::Iter<&str> { [].iter() }
}

/// Interface of a parser builder
pub trait RBuilder : Send + Sync {
    fn new(&self) -> Box<dyn RParser>;

    fn probe(&self, data:&[u8]) -> bool;
}

// status: return code, events

pub const R_STATUS_EVENTS : u32  = 0x0100;

pub const R_STATUS_OK : u32      = 0x0000;
pub const R_STATUS_FAIL : u32    = 0x0001;

pub const R_STATUS_EV_MASK : u32 = 0x0f00;
pub const R_STATUS_MASK : u32    = 0x00ff;

#[macro_export]
macro_rules! r_status_is_ok {
    ($status:expr) => { ($status & $crate::R_STATUS_MASK) == $crate::R_STATUS_MASK }
}

#[macro_export]
macro_rules! r_status_has_events {
    ($status:expr) => { ($status & $crate::R_STATUS_EV_MASK) == $crate::R_STATUS_EVENTS }
}

// Constants
pub const STREAM_TOSERVER : u8 = 0;
pub const STREAM_TOCLIENT : u8 = 1;

// Helper macros
// We can't define them (can we ?) in the trait, partly because of the 'no_mangle' and 'extern'
// stuff.
// This forces to use macros in addition to the trait, but at least provides a proper way of
// encapsulating the translation of C variables to rust.

#[macro_export]
macro_rules! r_declare_state_new {
    ($f:ident, $ty:ident, $args:expr) => {
        #[no_mangle]
        pub extern "C" fn $f() -> *mut libc::c_char {
            let b = Box::new($ty::new($args));
            unsafe { mem::transmute(b) }
        }
    }
}

#[macro_export]
macro_rules! r_declare_state_free {
    ($f:ident, $ty:ident, $expr:expr) => {
        impl<'a> Drop for $ty<'a> {
            fn drop(&mut self) {
                $expr
            }
        }

        #[no_mangle]
        pub unsafe extern fn $f(ptr: *mut libc::c_char)
        {
            let b: Box<$ty> = mem::transmute(ptr);
            // reference will be dropped automatically, and allocated memory
            // will be freed
            // but let's do it explicitly
            drop(b);
        }
    }
}

#[macro_export]
macro_rules! r_implement_probe {
    ($f:ident, $g:ident) => {
        #[no_mangle]
        pub extern "C" fn $f(input: *const c_char, input_len: u32, _offset: *const c_char) -> u32 {
            let data_len = input_len as usize;
            let data : &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, data_len) };
            let res = ::std::panic::catch_unwind(|| {
                match $g(data) {
                    true  => 1,
                    false => 0,
                }
            });
            match res {
                Ok(v)  => v,
                Err(_) => 0xffffffff,
            }
        }
    }
}

#[macro_export]
macro_rules! r_implement_parse {
    ($f:ident, $g:ident) => {
        #[no_mangle]
        pub extern "C" fn $f(direction: u8, input: *const c_char, input_len: u32, ptr: *mut $g) -> u32 {
            let data_len = input_len as usize;
            let data : &[u8] = unsafe { std::slice::from_raw_parts(input as *mut u8, data_len) };
            if ptr.is_null() { return 0xffffffff; };
            let res = ::std::panic::catch_unwind(|| {
                let parser = unsafe { &mut *ptr };
                parser.parse(data, direction)
            });
            match res {
                Ok(v)  => v,
                Err(_) => 0xffffffff,
            }
        }
    }
}

<|endoftext|>


<commit_before>use options::parser::{Arg, Args, Values, TakesValue};

// exa options
pub static VERSION: Arg = Arg { short: Some(b'v'), long: "version",  takes_value: TakesValue::Forbidden };
pub static HELP:    Arg = Arg { short: Some(b'?'), long: "help",     takes_value: TakesValue::Forbidden };

// display options
pub static ONE_LINE: Arg = Arg { short: Some(b'1'), long: "oneline",  takes_value: TakesValue::Forbidden };
pub static LONG:     Arg = Arg { short: Some(b'l'), long: "long",     takes_value: TakesValue::Forbidden };
pub static GRID:     Arg = Arg { short: Some(b'G'), long: "grid",     takes_value: TakesValue::Forbidden };
pub static ACROSS:   Arg = Arg { short: Some(b'x'), long: "across",   takes_value: TakesValue::Forbidden };
pub static RECURSE:  Arg = Arg { short: Some(b'R'), long: "recurse",  takes_value: TakesValue::Forbidden };
pub static TREE:     Arg = Arg { short: Some(b'T'), long: "tree",     takes_value: TakesValue::Forbidden };
pub static CLASSIFY: Arg = Arg { short: Some(b'F'), long: "classify", takes_value: TakesValue::Forbidden };

pub static COLOR:  Arg = Arg { short: None, long: "color",  takes_value: TakesValue::Necessary(Some(COLOURS)) };
pub static COLOUR: Arg = Arg { short: None, long: "colour", takes_value: TakesValue::Necessary(Some(COLOURS)) };
const COLOURS: &[&str] = &["always", "auto", "never"];

pub static COLOR_SCALE:  Arg = Arg { short: None, long: "color-scale",  takes_value: TakesValue::Forbidden };
pub static COLOUR_SCALE: Arg = Arg { short: None, long: "colour-scale", takes_value: TakesValue::Forbidden };

// filtering and sorting options
pub static ALL:         Arg = Arg { short: Some(b'a'), long: "all",         takes_value: TakesValue::Forbidden };
pub static LIST_DIRS:   Arg = Arg { short: Some(b'd'), long: "list-dirs",   takes_value: TakesValue::Forbidden };
pub static LEVEL:       Arg = Arg { short: Some(b'L'), long: "level",       takes_value: TakesValue::Necessary(None) };
pub static REVERSE:     Arg = Arg { short: Some(b'r'), long: "reverse",     takes_value: TakesValue::Forbidden };
pub static SORT:        Arg = Arg { short: Some(b's'), long: "sort",        takes_value: TakesValue::Necessary(Some(SORTS)) };
pub static IGNORE_GLOB: Arg = Arg { short: Some(b'I'), long: "ignore-glob", takes_value: TakesValue::Necessary(None) };
pub static GIT_IGNORE:  Arg = Arg { short: None, long: "git-ignore",           takes_value: TakesValue::Forbidden };
pub static DIRS_FIRST:  Arg = Arg { short: None, long: "group-directories-first",  takes_value: TakesValue::Forbidden };
pub static ONLY_DIRS:   Arg = Arg { short: None, long: "only-dirs", takes_value: TakesValue::Forbidden };
const SORTS: Values = &[ "name", "Name", "size", "extension",
                             "Extension", "modified", "accessed",
                             "created", "inode", "type", "none" ];

// display options
pub static BINARY:     Arg = Arg { short: Some(b'b'), long: "binary",     takes_value: TakesValue::Forbidden };
pub static BYTES:      Arg = Arg { short: Some(b'B'), long: "bytes",      takes_value: TakesValue::Forbidden };
pub static GROUP:      Arg = Arg { short: Some(b'g'), long: "group",      takes_value: TakesValue::Forbidden };
pub static HEADER:     Arg = Arg { short: Some(b'h'), long: "header",     takes_value: TakesValue::Forbidden };
pub static INODE:      Arg = Arg { short: Some(b'i'), long: "inode",      takes_value: TakesValue::Forbidden };
pub static LINKS:      Arg = Arg { short: Some(b'H'), long: "links",      takes_value: TakesValue::Forbidden };
pub static MODIFIED:   Arg = Arg { short: Some(b'm'), long: "modified",   takes_value: TakesValue::Forbidden };
pub static BLOCKS:     Arg = Arg { short: Some(b'S'), long: "blocks",     takes_value: TakesValue::Forbidden };
pub static TIME:       Arg = Arg { short: Some(b't'), long: "time",       takes_value: TakesValue::Necessary(Some(TIMES)) };
pub static ACCESSED:   Arg = Arg { short: Some(b'u'), long: "accessed",   takes_value: TakesValue::Forbidden };
pub static CREATED:    Arg = Arg { short: Some(b'U'), long: "created",    takes_value: TakesValue::Forbidden };
pub static TIME_STYLE: Arg = Arg { short: None,       long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
const TIMES: Values = &["modified", "accessed", "created"];
const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso"];

// optional feature options
pub static GIT:       Arg = Arg { short: None,       long: "git",      takes_value: TakesValue::Forbidden };
pub static EXTENDED:  Arg = Arg { short: Some(b'@'), long: "extended", takes_value: TakesValue::Forbidden };

pub static ALL_ARGS: Args = Args(&[
    &VERSION, &HELP,

    &ONE_LINE, &LONG, &GRID, &ACROSS, &RECURSE, &TREE, &CLASSIFY,
    &COLOR, &COLOUR, &COLOR_SCALE, &COLOUR_SCALE,

    &ALL, &LIST_DIRS, &LEVEL, &REVERSE, &SORT, &DIRS_FIRST,
    &IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS,

    &BINARY, &BYTES, &GROUP, &HEADER, &INODE, &LINKS, &MODIFIED, &BLOCKS,
    &TIME, &ACCESSED, &CREATED, &TIME_STYLE,

    &GIT, &EXTENDED,
]);

<commit_msg>Add short option for only-dirs<commit_after>use options::parser::{Arg, Args, Values, TakesValue};

// exa options
pub static VERSION: Arg = Arg { short: Some(b'v'), long: "version",  takes_value: TakesValue::Forbidden };
pub static HELP:    Arg = Arg { short: Some(b'?'), long: "help",     takes_value: TakesValue::Forbidden };

// display options
pub static ONE_LINE: Arg = Arg { short: Some(b'1'), long: "oneline",  takes_value: TakesValue::Forbidden };
pub static LONG:     Arg = Arg { short: Some(b'l'), long: "long",     takes_value: TakesValue::Forbidden };
pub static GRID:     Arg = Arg { short: Some(b'G'), long: "grid",     takes_value: TakesValue::Forbidden };
pub static ACROSS:   Arg = Arg { short: Some(b'x'), long: "across",   takes_value: TakesValue::Forbidden };
pub static RECURSE:  Arg = Arg { short: Some(b'R'), long: "recurse",  takes_value: TakesValue::Forbidden };
pub static TREE:     Arg = Arg { short: Some(b'T'), long: "tree",     takes_value: TakesValue::Forbidden };
pub static CLASSIFY: Arg = Arg { short: Some(b'F'), long: "classify", takes_value: TakesValue::Forbidden };

pub static COLOR:  Arg = Arg { short: None, long: "color",  takes_value: TakesValue::Necessary(Some(COLOURS)) };
pub static COLOUR: Arg = Arg { short: None, long: "colour", takes_value: TakesValue::Necessary(Some(COLOURS)) };
const COLOURS: &[&str] = &["always", "auto", "never"];

pub static COLOR_SCALE:  Arg = Arg { short: None, long: "color-scale",  takes_value: TakesValue::Forbidden };
pub static COLOUR_SCALE: Arg = Arg { short: None, long: "colour-scale", takes_value: TakesValue::Forbidden };

// filtering and sorting options
pub static ALL:         Arg = Arg { short: Some(b'a'), long: "all",         takes_value: TakesValue::Forbidden };
pub static LIST_DIRS:   Arg = Arg { short: Some(b'd'), long: "list-dirs",   takes_value: TakesValue::Forbidden };
pub static LEVEL:       Arg = Arg { short: Some(b'L'), long: "level",       takes_value: TakesValue::Necessary(None) };
pub static REVERSE:     Arg = Arg { short: Some(b'r'), long: "reverse",     takes_value: TakesValue::Forbidden };
pub static SORT:        Arg = Arg { short: Some(b's'), long: "sort",        takes_value: TakesValue::Necessary(Some(SORTS)) };
pub static IGNORE_GLOB: Arg = Arg { short: Some(b'I'), long: "ignore-glob", takes_value: TakesValue::Necessary(None) };
pub static GIT_IGNORE:  Arg = Arg { short: None, long: "git-ignore",           takes_value: TakesValue::Forbidden };
pub static DIRS_FIRST:  Arg = Arg { short: None, long: "group-directories-first",  takes_value: TakesValue::Forbidden };
pub static ONLY_DIRS:   Arg = Arg { short: Some(b'D'), long: "only-dirs", takes_value: TakesValue::Forbidden };
const SORTS: Values = &[ "name", "Name", "size", "extension",
                             "Extension", "modified", "accessed",
                             "created", "inode", "type", "none" ];

// display options
pub static BINARY:     Arg = Arg { short: Some(b'b'), long: "binary",     takes_value: TakesValue::Forbidden };
pub static BYTES:      Arg = Arg { short: Some(b'B'), long: "bytes",      takes_value: TakesValue::Forbidden };
pub static GROUP:      Arg = Arg { short: Some(b'g'), long: "group",      takes_value: TakesValue::Forbidden };
pub static HEADER:     Arg = Arg { short: Some(b'h'), long: "header",     takes_value: TakesValue::Forbidden };
pub static INODE:      Arg = Arg { short: Some(b'i'), long: "inode",      takes_value: TakesValue::Forbidden };
pub static LINKS:      Arg = Arg { short: Some(b'H'), long: "links",      takes_value: TakesValue::Forbidden };
pub static MODIFIED:   Arg = Arg { short: Some(b'm'), long: "modified",   takes_value: TakesValue::Forbidden };
pub static BLOCKS:     Arg = Arg { short: Some(b'S'), long: "blocks",     takes_value: TakesValue::Forbidden };
pub static TIME:       Arg = Arg { short: Some(b't'), long: "time",       takes_value: TakesValue::Necessary(Some(TIMES)) };
pub static ACCESSED:   Arg = Arg { short: Some(b'u'), long: "accessed",   takes_value: TakesValue::Forbidden };
pub static CREATED:    Arg = Arg { short: Some(b'U'), long: "created",    takes_value: TakesValue::Forbidden };
pub static TIME_STYLE: Arg = Arg { short: None,       long: "time-style", takes_value: TakesValue::Necessary(Some(TIME_STYLES)) };
const TIMES: Values = &["modified", "accessed", "created"];
const TIME_STYLES: Values = &["default", "long-iso", "full-iso", "iso"];

// optional feature options
pub static GIT:       Arg = Arg { short: None,       long: "git",      takes_value: TakesValue::Forbidden };
pub static EXTENDED:  Arg = Arg { short: Some(b'@'), long: "extended", takes_value: TakesValue::Forbidden };

pub static ALL_ARGS: Args = Args(&[
    &VERSION, &HELP,

    &ONE_LINE, &LONG, &GRID, &ACROSS, &RECURSE, &TREE, &CLASSIFY,
    &COLOR, &COLOUR, &COLOR_SCALE, &COLOUR_SCALE,

    &ALL, &LIST_DIRS, &LEVEL, &REVERSE, &SORT, &DIRS_FIRST,
    &IGNORE_GLOB, &GIT_IGNORE, &ONLY_DIRS,

    &BINARY, &BYTES, &GROUP, &HEADER, &INODE, &LINKS, &MODIFIED, &BLOCKS,
    &TIME, &ACCESSED, &CREATED, &TIME_STYLE,

    &GIT, &EXTENDED,
]);

<|endoftext|>


<commit_before>
use token::{Token, TokenKind};
use error::{NcclError, ErrorKind};

// ranked worst to least
enum Indent {
    Neither,
    Tabs,
    Spaces(u8),
}

pub struct Scanner {
    source: Vec<u8>,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: u64,
    indent: Indent,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source.into_bytes(),
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
            indent: Indent::Neither,
        }
    }

    pub fn scan_tokens(&mut self) -> Result<Vec<Token>, NcclError> {
        while !self.is_at_end() {
            self.start = self.current;
            match self.scan_token() {
                Ok(_) => {},
                Err(e) => return Err(e)
            }
        }

        self.tokens.push(Token::new(TokenKind::EOF, "".into(), self.line));
        Ok(self.tokens.clone())
    }

    fn scan_token(&mut self) -> Result<(), NcclError> {
        let mut error = Ok(());
        match self.advance() {
            b':' => {
                self.add_token(TokenKind::Colon);
                while self.peek() == b' ' && !self.is_at_end() {
                    self.advance();
                }
                if self.is_at_end() || self.peek() == b'\n' {
                    error = Err(NcclError::new(ErrorKind::ParseError, "Expected schema name, found EOF", self.line));
                }
            },

            b'#' => {
                while self.peek() != b'\n' && !self.is_at_end() {
                    self.advance();
                }
            },

            b' ' => {
                match self.indent {
                    Indent::Neither => {
                        let mut spaces = 0;
                        while self.peek() == b' ' && !self.is_at_end() {
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.indent = Indent::Spaces(spaces);
                        self.add_token(TokenKind::Indent);
                    }
                    Indent::Spaces(s) => {
                        let mut spaces = 0;
                        while spaces < s  && !self.is_at_end() {
                            if self.peek() != b' ' {
                                error = Err(NcclError::new(ErrorKind::IndentationError, &format!("Incorrect number of spaces: found {}, expected {}", spaces, s), self.line));
                            }
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Tabs => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected tabs, found spaces", self.line)); }
                }
            },

            b'\t' => {
                match self.indent {
                    Indent::Neither => {
                        self.add_token(TokenKind::Indent);
                        self.indent = Indent::Tabs;
                    },
                    Indent::Tabs => {
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Spaces(_) => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected spaces, found tabs", self.line)); }
                }
            },

            b'\n' => self.line += 1,

            b'\r' => {},

            b'"' => if let Err(e) = self.string() { error = Err(e); },

            _ => if let Err(e) = self.identifier() { error = Err(e); },
        }

        error
    }

    fn identifier(&mut self) -> Result<(), NcclError> {
        while self.peek() != b'\n' && self.peek() != b'\r' && self.peek() != b':' && !self.is_at_end() {
            self.advance();
        }

        let value = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.add_token_string(TokenKind::Name, value);

        Ok(())
    }

    fn string(&mut self) -> Result<(), NcclError> {
        let mut value = String::new();
        while self.peek() != b'"' && !self.is_at_end() {
            if self.peek() == b'\n' {
                self.line += 1;
            }

            if self.peek() == b'\\' {
                self.advance();
                match self.peek() {
                    b'n' => {
                        value.push('\n');
                    },
                    b'r' => {
                        value.push('\r');
                    }
                    b'\\' => {
                        value.push('\\');
                    },
                    b'"' => {
                        value.push('"');
                    },
                    _ => return Err(NcclError::new(ErrorKind::ParseError, &format!("Unknown format code: {}", self.peek()), self.line))
                }
            } else {
                value.push(self.source[self.current] as char);
            }

            self.advance();
        }

        if self.is_at_end() {
            return Err(NcclError::new(ErrorKind::ParseError, "Unterminated string", self.line));
        }

        self.advance();

        //let value = String::from_utf8(self.source[self.start + 1..self.current - 1].to_vec()).unwrap();
        self.add_token_string(TokenKind::Name, value);

        Ok(())
    }

    fn is_at_end(&mut self) -> bool {
        self.current >= self.source.len()
    }

    fn advance(&mut self) -> u8 {
        self.current += 1;
        self.source[self.current - 1]
    }

    fn add_token(&mut self, kind: TokenKind) {
        let text = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.tokens.push(Token::new(kind, text, self.line));
    }

    fn add_token_string(&mut self, kind: TokenKind, value: String) {
        self.tokens.push(Token::new(kind, value, self.line));
    }

    fn peek(&mut self) -> u8 {
        if self.current >= self.source.len() {
            b'\0'
        } else {
            self.source[self.current]
        }
    }
}

<commit_msg>really not sure what happened there...<commit_after>
use token::{Token, TokenKind};
use error::{NcclError, ErrorKind};

// ranked worst to least
enum Indent {
    Neither,
    Tabs,
    Spaces(u8),
}

pub struct Scanner {
    source: Vec<u8>,
    tokens: Vec<Token>,
    start: usize,
    current: usize,
    line: u64,
    indent: Indent,
}

impl Scanner {
    pub fn new(source: String) -> Self {
        Scanner {
            source: source.into_bytes(),
            tokens: Vec::new(),
            start: 0,
            current: 0,
            line: 1,
            indent: Indent::Neither,
        }
    }

    pub fn scan_tokens(&mut self) -> Result<Vec<Token>, NcclError> {
        while !self.is_at_end() {
            self.start = self.current;
            match self.scan_token() {
                Ok(_) => {},
                Err(e) => return Err(e)
            }
        }

        self.tokens.push(Token::new(TokenKind::EOF, "".into(), self.line));
        Ok(self.tokens.clone())
    }

    fn scan_token(&mut self) -> Result<(), NcclError> {
        let mut error = Ok(());
        match self.advance() {
            b':' => {
                self.add_token(TokenKind::Colon);
                while self.peek() == b' ' && !self.is_at_end() {
                    self.advance();
                }
                if self.is_at_end() || self.peek() == b'\n' {
                    error = Err(NcclError::new(ErrorKind::ParseError, "Expected schema name, found EOF", self.line));
                }
            },

            b'#' => {
                while self.peek() != b'\n' && !self.is_at_end() {
                    self.advance();
                }
            },

            b' ' => {
                match self.indent {
                    Indent::Neither => {
                        let mut spaces = 0;
                        while self.peek() == b' ' && !self.is_at_end() {
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.indent = Indent::Spaces(spaces);
                        self.add_token(TokenKind::Indent);
                    }
                    Indent::Spaces(s) => {
                        let mut spaces = 0;
                        while spaces < s  && !self.is_at_end() {
                            if self.peek() != b' ' {
                                error = Err(NcclError::new(ErrorKind::IndentationError, &format!("Incorrect number of spaces: found {}, expected {}", spaces, s), self.line));
                            }
                            self.advance();
                            spaces += 1;
                        }
                        if self.is_at_end() {
                            error = Err(NcclError::new(ErrorKind::ParseError, "Expected value, found EOF", self.line));
                        }
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Tabs => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected tabs, found spaces", self.line)); }
                }
            },

            b'\t' => {
                match self.indent {
                    Indent::Neither => {
                        self.add_token(TokenKind::Indent);
                        self.indent = Indent::Tabs;
                    },
                    Indent::Tabs => {
                        self.add_token(TokenKind::Indent);
                    },
                    Indent::Spaces(_) => { error = Err(NcclError::new(ErrorKind::IndentationError, "Expected spaces, found tabs", self.line)); }
                }
            },

            b'\n' => self.line += 1,

            b'\r' => {},

            b'"' => if let Err(e) = self.string() { error = Err(e); },

            _ => if let Err(e) = self.identifier() { error = Err(e); },
        }

        error
    }

    fn identifier(&mut self) -> Result<(), NcclError> {
        while self.peek() != b'\n' && self.peek() != b'\r' && self.peek() != b':' && !self.is_at_end() {
            self.advance();
        }

        let value = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.add_token_string(TokenKind::Name, value);

        Ok(())
    }

    fn string(&mut self) -> Result<(), NcclError> {
        let mut value = String::new();
        while self.peek() != b'"' && !self.is_at_end() {
            if self.peek() == b'\n' {
                self.line += 1;
            }

            if self.peek() == b'\\' {
                self.advance();
                match self.peek() {
                    b'n' => {
                        value.push('\n');
                    },
                    b'r' => {
                        value.push('\r');
                    }
                    b'\\' => {
                        value.push('\\');
                    },
                    b'"' => {
                        value.push('"');
                    },
                    _ => return Err(NcclError::new(ErrorKind::ParseError, &format!("Unknown format code: {}", self.peek()), self.line))
                }
            } else {
                value.push(self.source[self.current] as char);
            }

            self.advance();
        }

        if self.is_at_end() {
            return Err(NcclError::new(ErrorKind::ParseError, "Unterminated string", self.line));
        }

        self.advance();

        self.add_token_string(TokenKind::Name, value);

        Ok(())
    }

    fn is_at_end(&mut self) -> bool {
        self.current >= self.source.len()
    }

    fn advance(&mut self) -> u8 {
        self.current += 1;
        self.source[self.current - 1]
    }

    fn add_token(&mut self, kind: TokenKind) {
        let text = String::from_utf8(self.source[self.start..self.current].to_vec()).unwrap();
        self.tokens.push(Token::new(kind, text, self.line));
    }

    fn add_token_string(&mut self, kind: TokenKind, value: String) {
        self.tokens.push(Token::new(kind, value, self.line));
    }

    fn peek(&mut self) -> u8 {
        if self.current >= self.source.len() {
            b'\0'
        } else {
            self.source[self.current]
        }
    }
}

<|endoftext|>


<commit_before>bitflags! {
    pub struct Flags : u8 {
        const BACKSL = 1;
        const SQUOTE = 2;
        const DQUOTE = 4;
        const TRIM   = 8;
    }
}

pub struct QuoteTerminator {
    buffer: String,
    eof:    Option<String>,
    eof_buffer: String,
    read:   usize,
    flags:  Flags,
}

impl QuoteTerminator {
    pub fn new(input: String) -> QuoteTerminator {
        QuoteTerminator { buffer: input, eof: None, eof_buffer: String::new(), read: 0, flags: Flags::empty() }
    }

    pub fn append(&mut self, input: String) {
        if self.eof.is_none() {
            self.buffer.push_str(if self.flags.contains(TRIM) { input.trim() } else { &input });
        } else {
            self.eof_buffer.push_str(&input);
        }
    }

    pub fn check_termination(&mut self) -> bool {
        let mut eof_line = None;
        let eof = self.eof.clone();
        let status = if let Some(ref eof) = eof {
            let line = &self.eof_buffer;
            eof_line = Some([&line, "\n"].concat());
            line.trim() == eof
        } else {
            {
                let mut eof_found = false;
                {
                    let mut bytes = self.buffer.bytes().skip(self.read);
                    while let Some(character) = bytes.next() {
                        self.read += 1;
                        match character {
                            _ if self.flags.contains(BACKSL) => self.flags ^= BACKSL,
                            b'\\'                            => self.flags ^= BACKSL,
                            b'\'' if !self.flags.intersects(DQUOTE) => self.flags ^= SQUOTE,
                            b'"'  if !self.flags.intersects(SQUOTE) => self.flags ^= DQUOTE,
                            b'<' if !self.flags.contains(SQUOTE | DQUOTE) => {
                                let as_bytes = self.buffer.as_bytes();
                                if Some(&b'<') == as_bytes.get(self.read) {
                                    self.read += 1;
                                    if Some(&b'<') != as_bytes.get(self.read) {
                                        use std::str;
                                        let eof_phrase = unsafe { str::from_utf8_unchecked(&as_bytes[self.read..]) };
                                        self.eof = Some(eof_phrase.trim().to_owned());
                                        eof_found = true;
                                        break
                                    }
                                }
                            }
                            _ => (),
                        }
                    }
                }
                if eof_found {
                    self.buffer.push('\n');
                    return false
                }
            }

            if self.flags.intersects(SQUOTE | DQUOTE) {
                self.read += 1;
                self.buffer.push('\n');
                false
            } else {
                if let Some(b'\\') = self.buffer.bytes().last() {
                    let _ = self.buffer.pop();
                    self.read -= 1;
                    self.flags |= TRIM;
                    false
                } else {
                    // If the last two bytes are either '&&' or '||', we aren't terminated yet.
                    let bytes = self.buffer.as_bytes();
                    if bytes.len() >= 2 {
                        let bytes = &bytes[bytes.len()-2..];
                        bytes != &[b'&', b'&'] && bytes != &[b'|', b'|']
                    } else {
                        false
                    }
                }
            }
        };

        if let Some(line) = eof_line {
            self.buffer.push_str(&line);
        }
        if self.eof.is_some() {
            self.eof_buffer.clear();
            if status {
                self.eof = None;
            }
        }
        status
    }

    pub fn consume(self) -> String { self.buffer }
}
<commit_msg>Fix Quote Terminator<commit_after>bitflags! {
    pub struct Flags : u8 {
        const BACKSL = 1;
        const SQUOTE = 2;
        const DQUOTE = 4;
        const TRIM   = 8;
    }
}

pub struct QuoteTerminator {
    buffer: String,
    eof:    Option<String>,
    eof_buffer: String,
    read:   usize,
    flags:  Flags,
}

impl QuoteTerminator {
    pub fn new(input: String) -> QuoteTerminator {
        QuoteTerminator { buffer: input, eof: None, eof_buffer: String::new(), read: 0, flags: Flags::empty() }
    }

    pub fn append(&mut self, input: String) {
        if self.eof.is_none() {
            self.buffer.push_str(if self.flags.contains(TRIM) { input.trim() } else { &input });
        } else {
            self.eof_buffer.push_str(&input);
        }
    }

    pub fn check_termination(&mut self) -> bool {
        let mut eof_line = None;
        let eof = self.eof.clone();
        let status = if let Some(ref eof) = eof {
            let line = &self.eof_buffer;
            eof_line = Some([&line, "\n"].concat());
            line.trim() == eof
        } else {
            {
                let mut eof_found = false;
                {
                    let mut bytes = self.buffer.bytes().skip(self.read);
                    while let Some(character) = bytes.next() {
                        self.read += 1;
                        match character {
                            _ if self.flags.contains(BACKSL) => self.flags ^= BACKSL,
                            b'\\'                            => self.flags ^= BACKSL,
                            b'\'' if !self.flags.intersects(DQUOTE) => self.flags ^= SQUOTE,
                            b'"'  if !self.flags.intersects(SQUOTE) => self.flags ^= DQUOTE,
                            b'<' if !self.flags.contains(SQUOTE | DQUOTE) => {
                                let as_bytes = self.buffer.as_bytes();
                                if Some(&b'<') == as_bytes.get(self.read) {
                                    self.read += 1;
                                    if Some(&b'<') != as_bytes.get(self.read) {
                                        use std::str;
                                        let eof_phrase = unsafe { str::from_utf8_unchecked(&as_bytes[self.read..]) };
                                        self.eof = Some(eof_phrase.trim().to_owned());
                                        eof_found = true;
                                        break
                                    }
                                }
                            }
                            _ => (),
                        }
                    }
                }
                if eof_found {
                    self.buffer.push('\n');
                    return false
                }
            }

            if self.flags.intersects(SQUOTE | DQUOTE) {
                self.read += 1;
                self.buffer.push('\n');
                false
            } else {
                if let Some(b'\\') = self.buffer.bytes().last() {
                    let _ = self.buffer.pop();
                    self.read -= 1;
                    self.flags |= TRIM;
                    false
                } else {
                    // If the last two bytes are either '&&' or '||', we aren't terminated yet.
                    let bytes = self.buffer.as_bytes();
                    if bytes.len() >= 2 {
                        let bytes = &bytes[bytes.len()-2..];
                        bytes != &[b'&', b'&'] && bytes != &[b'|', b'|']
                    } else {
                        true
                    }
                }
            }
        };

        if let Some(line) = eof_line {
            self.buffer.push_str(&line);
        }
        if self.eof.is_some() {
            self.eof_buffer.clear();
            if status {
                self.eof = None;
            }
        }
        status
    }

    pub fn consume(self) -> String { self.buffer }
}
<|endoftext|>


<commit_before>use crate::utils;
use dirs;
use std::fs;
use std::io::ErrorKind;
use std::process::Command;

pub fn setup() {
    // Environment (Unix)

    utils::install_package("ripgrep");

    // Version control (Git)

    utils::install_package("git");

    let home_path = match dirs::home_dir() {
        Some(directory) => directory,
        None => panic!("Cannot find the home directory."),
    };

    let repos_path = home_path.join("repos");

    match fs::read_dir(&repos_path) {
        Ok(_) => println!("The repos are already installed."),
        Err(_) => {
            utils::clone_repo(&repos_path, "https://github.com/trevordmiller/scripts");
            utils::clone_repo(&repos_path, "https://github.com/trevordmiller/study");
        }
    };

    // Editor (Vim)

    utils::install_package("vim");

    let editor_plugins_path = home_path
        .join(".vim")
        .join("pack")
        .join("plugins")
        .join("start");

    utils::create_dir(&editor_plugins_path);

    let editor_configuration_path = home_path.join(".vimrc");

    utils::create_file(&editor_configuration_path);

    match fs::read_dir(&editor_plugins_path) {
        Ok(_) => println!("The editor plugins are already installed."),
        Err(_) => {
            utils::clone_repo(&editor_plugins_path, "https://github.com/tpope/vim-sensible");
            utils::clone_repo(&editor_plugins_path, "https://github.com/tpope/vim-sleuth");
            utils::clone_repo(&editor_plugins_path, "https://github.com/sheerun/vim-polyglot");
            utils::clone_repo(&editor_plugins_path, "https://github.com/octref/RootIgnore");
            utils::clone_repo(&editor_plugins_path, "https://github.com/dense-analysis/ale");
            utils::clone_repo(&editor_plugins_path, "https://github.com/arcticicestudio/nord-vim");

            println!("Adding editor configuration.");
            match fs::write(&editor_configuration_path, "set grepprg=rg\\ --vimgrep\nset grepformat=%f:%l:%c:%m\ncolorscheme nord") {
                Ok(_) => (),
                Err(error) => panic!("There was a problem: {:?}", error),
            }
        }
    };

    // Application programming (JavaScript)

    utils::install_package("node");

    // Systems programming (Rust)

    utils::install_package("rustup-init");

    let rustup_path_check = match Command::new("which").arg("rustup").output() {
        Ok(output) => output.stdout,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    if rustup_path_check.is_empty() {
        match Command::new("rustup-init").output() {
            Ok(_) => (),
            Err(error) => match error.kind() {
                ErrorKind::NotFound => panic!("The rustup-init command is missing."),
                other_error => panic!("There was a problem: {:?}", other_error),
            },
        }
    } else {
        println!("The rustup-init has already been run.")
    }
}

pub fn upgrade() {
    // Environment (Unix)

    println!("Upgrading package manager.");

    match Command::new("brew").arg("update").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    utils::upgrade_package("ripgrep");

    // Version control (Git)

    utils::upgrade_package("git");

    // Editor (Vim)

    utils::upgrade_package("vim");

    println!("Upgrading editor plugins.");

    let home_path = match dirs::home_dir() {
        Some(directory) => directory,
        None => panic!("Cannot find the home directory."),
    };

    let editor_plugins_path = home_path
        .join(".vim")
        .join("pack")
        .join("plugins")
        .join("start");

    match fs::read_dir(&editor_plugins_path) {
        Ok(paths) => {
            for path in paths {
                match Command::new("git")
                    .current_dir(match path {
                        Ok(path) => path.path(),
                        Err(error) => panic!("There was a problem: {:?}", error),
                    })
                    .arg("pull")
                    .output()
                {
                    Ok(_) => (),
                    Err(error) => panic!("There was a problem: {:?}", error),
                }
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    // Application programming (JavaScript)

    utils::upgrade_package("node");

    // Systems programming (Rust)

    utils::upgrade_package("rustup-init");

    println!("Upgrading systems programming toolchain.");

    match Command::new("rustup").arg("update").output() {
        Ok(_) => (),
        Err(error) => match error.kind() {
            ErrorKind::NotFound => panic!("The rustup command is missing."),
            other_error => panic!("There was a problem: {:?}", other_error),
        },
    }
}

pub fn end() {
    // Environment (Unix)

    println!("Removing package manager artifacts.");

    match Command::new("brew").arg("cleanup").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    // Version control (Git)

    println!("Making sure all repos are clean.");

    let home_path = match dirs::home_dir() {
        Some(directory) => directory,
        None => panic!("Cannot find the home directory."),
    };

    let repos_path = home_path.join("repos");

    match fs::read_dir(&repos_path) {
        Ok(paths) => {
            let mut all_repos_clean = true;

            for path in paths {
                let path = match &path {
                    Ok(path) => path.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let status_check = match Command::new("git")
                    .current_dir(&path)
                    .arg("status")
                    .arg("--porcelain")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let unpushed_check = match Command::new("git")
                    .current_dir(&path)
                    .arg("log")
                    .arg("@{u}..")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                if !status_check.is_empty() || !unpushed_check.is_empty() {
                    all_repos_clean = false;
                    eprintln!("A dirty repo was found: {}", &path.display());
                }
            }

            if !all_repos_clean {
                panic!("Repos are dirty.");
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    // Editor (Vim)

    println!("Quitting editor processes.");

    match Command::new("killall").arg("vim").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    // Application programming (JavaScript)

    println!("Quitting application programming processes.");

    match Command::new("killall").arg("node").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    // Systems programming (Rust)

    println!("Quitting systems programming processes.");

    match Command::new("killall").arg("rls").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}
<commit_msg>Remove pieces now in readme prerequisites<commit_after>use crate::utils;
use dirs;
use std::fs;
use std::io::ErrorKind;
use std::process::Command;

pub fn setup() {
    // Environment (Unix)

    utils::install_package("ripgrep");

    // Version control (Git)

    utils::install_package("git");

    let home_path = match dirs::home_dir() {
        Some(directory) => directory,
        None => panic!("Cannot find the home directory."),
    };

    // Editor (Vim)

    utils::install_package("vim");

    let editor_plugins_path = home_path
        .join(".vim")
        .join("pack")
        .join("plugins")
        .join("start");

    utils::create_dir(&editor_plugins_path);

    let editor_configuration_path = home_path.join(".vimrc");

    utils::create_file(&editor_configuration_path);

    match fs::read_dir(&editor_plugins_path) {
        Ok(_) => println!("The editor plugins are already installed."),
        Err(_) => {
            utils::clone_repo(&editor_plugins_path, "https://github.com/tpope/vim-sensible");
            utils::clone_repo(&editor_plugins_path, "https://github.com/tpope/vim-sleuth");
            utils::clone_repo(&editor_plugins_path, "https://github.com/sheerun/vim-polyglot");
            utils::clone_repo(&editor_plugins_path, "https://github.com/octref/RootIgnore");
            utils::clone_repo(&editor_plugins_path, "https://github.com/dense-analysis/ale");
            utils::clone_repo(&editor_plugins_path, "https://github.com/arcticicestudio/nord-vim");

            println!("Adding editor configuration.");
            match fs::write(&editor_configuration_path, "set grepprg=rg\\ --vimgrep\nset grepformat=%f:%l:%c:%m\ncolorscheme nord") {
                Ok(_) => (),
                Err(error) => panic!("There was a problem: {:?}", error),
            }
        }
    };

    // Application programming (JavaScript)

    utils::install_package("node");

    // Systems programming (Rust)

    utils::install_package("rustup-init");

    let rustup_path_check = match Command::new("which").arg("rustup").output() {
        Ok(output) => output.stdout,
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    if rustup_path_check.is_empty() {
        match Command::new("rustup-init").output() {
            Ok(_) => (),
            Err(error) => match error.kind() {
                ErrorKind::NotFound => panic!("The rustup-init command is missing."),
                other_error => panic!("There was a problem: {:?}", other_error),
            },
        }
    } else {
        println!("The rustup-init has already been run.")
    }
}

pub fn upgrade() {
    // Environment (Unix)

    println!("Upgrading package manager.");

    match Command::new("brew").arg("update").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    utils::upgrade_package("ripgrep");

    // Version control (Git)

    utils::upgrade_package("git");

    // Editor (Vim)

    utils::upgrade_package("vim");

    println!("Upgrading editor plugins.");

    let home_path = match dirs::home_dir() {
        Some(directory) => directory,
        None => panic!("Cannot find the home directory."),
    };

    let editor_plugins_path = home_path
        .join(".vim")
        .join("pack")
        .join("plugins")
        .join("start");

    match fs::read_dir(&editor_plugins_path) {
        Ok(paths) => {
            for path in paths {
                match Command::new("git")
                    .current_dir(match path {
                        Ok(path) => path.path(),
                        Err(error) => panic!("There was a problem: {:?}", error),
                    })
                    .arg("pull")
                    .output()
                {
                    Ok(_) => (),
                    Err(error) => panic!("There was a problem: {:?}", error),
                }
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    // Application programming (JavaScript)

    utils::upgrade_package("node");

    // Systems programming (Rust)

    utils::upgrade_package("rustup-init");

    println!("Upgrading systems programming toolchain.");

    match Command::new("rustup").arg("update").output() {
        Ok(_) => (),
        Err(error) => match error.kind() {
            ErrorKind::NotFound => panic!("The rustup command is missing."),
            other_error => panic!("There was a problem: {:?}", other_error),
        },
    }
}

pub fn end() {
    // Environment (Unix)

    println!("Removing package manager artifacts.");

    match Command::new("brew").arg("cleanup").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    // Version control (Git)

    println!("Making sure all repos are clean.");

    let home_path = match dirs::home_dir() {
        Some(directory) => directory,
        None => panic!("Cannot find the home directory."),
    };

    let repos_path = home_path.join("repos");

    match fs::read_dir(&repos_path) {
        Ok(paths) => {
            let mut all_repos_clean = true;

            for path in paths {
                let path = match &path {
                    Ok(path) => path.path(),
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let status_check = match Command::new("git")
                    .current_dir(&path)
                    .arg("status")
                    .arg("--porcelain")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                let unpushed_check = match Command::new("git")
                    .current_dir(&path)
                    .arg("log")
                    .arg("@{u}..")
                    .output()
                {
                    Ok(output) => output.stdout,
                    Err(error) => panic!("There was a problem: {:?}", error),
                };

                if !status_check.is_empty() || !unpushed_check.is_empty() {
                    all_repos_clean = false;
                    eprintln!("A dirty repo was found: {}", &path.display());
                }
            }

            if !all_repos_clean {
                panic!("Repos are dirty.");
            }
        }
        Err(error) => panic!("There was a problem: {:?}", error),
    };

    // Editor (Vim)

    println!("Quitting editor processes.");

    match Command::new("killall").arg("vim").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    // Application programming (JavaScript)

    println!("Quitting application programming processes.");

    match Command::new("killall").arg("node").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }

    // Systems programming (Rust)

    println!("Quitting systems programming processes.");

    match Command::new("killall").arg("rls").output() {
        Ok(_) => (),
        Err(error) => panic!("There was a problem: {:?}", error),
    }
}
<|endoftext|>