match-commutative 0.1.0

Match on patterns commutatively, reducing the use of duplicated patterns. ↔️
Documentation
use match_commutative::match_commutative;

#[test]
fn match_commutative_plus() {
    let operant1 = Operant::Str(Some("42".into()));
    let operant2 = Operant::Num(Some(1));
    let operator = Operator::Plus;

    let result = match_commutative!(
        operant2,
        operator,
        operant1,
        Operant::Str(Some(op_str)),
        Operator::Plus,
        Operant::Num(Some(op_num)) => {
            op_str.parse::<isize>().unwrap() + op_num
        },
        Operant::Str(_),
        Operator::Mult,
        Operant::Num(_) => {
            unreachable!()
        }
        non_commut {
            _,_,_ => {
                unreachable!()
            }
        }
    );

    assert_eq!(43, result);
}

#[test]
fn match_commutative_mult() {
    let operant1 = Operant::Str(Some("42".into()));
    let operant2 = Operant::Num(Some(2));
    let operator = Operator::Mult;

    let result = match_commutative!(
        operant2,
        operator,
        operant1,
        Operant::Str(_),
        Operator::Plus,
        Operant::Num(_) => {
            unreachable!()
        },
        Operant::Str(Some(op_str)),
        Operator::Mult,
        Operant::Num(Some(op_num)) => {
            op_str.parse::<isize>().unwrap() * op_num
        }
        non_commut {
            _,_,_ => {
                unreachable!()
            }
        }
    );

    assert_eq!(84, result);
}

#[test]
fn match_commutative_with_if_expr() {
    let operant1 = Operant::Str(Some("42".into()));
    let operant2 = Operant::Num(Some(2));
    let operator = Operator::Mult;

    let result = match_commutative!(
        operant2,
        operator,
        operant1,
        Operant::Str(Some(op_str)),
        Operator::Plus,
        Operant::Num(_) if op_str.len() < 3 => {
            unreachable!()
        },
        Operant::Str(Some(op_str)),
        Operator::Mult,
        Operant::Num(Some(op_num)) if op_str.len() < 3 => {
            op_str.parse::<isize>().unwrap() * op_num
        }
        non_commut {
            _,_,_ => {
                unreachable!()
            }
        }
    );

    assert_eq!(84, result);
}

#[test]
fn match_commutative_minus_within_non_commut_block() {
    let operant1 = Operant::Str(Some("42".into()));
    let operant2 = Operant::Num(Some(2));
    let operator = Operator::Minus;

    let result = match_commutative!(
        &operant1,
        &operator,
        &operant2,
        Operant::Str(_),
        Operator::Plus,
        Operant::Num(_) => {
            unreachable!()
        }
        non_commut {
            Operant::Str(Some(op_str)),
            Operator::Minus,
            Operant::Num(Some(op_num)) => {
                op_str.parse::<isize>().unwrap() - op_num
            },
            _,_,_ => {
                unreachable!()
            }
        }
    );

    assert_eq!(40, result);

    let result = match_commutative!(
        operant2,
        operator,   // operant1 and operant2 are swapped
        operant1,
        Operant::Str(_),
        Operator::Plus,
        Operant::Num(_) => {
            unreachable!()
        }
        non_commut {
            Operant::Num(Some(op_num)),
            Operator::Minus,
            Operant::Str(Some(op_str)) => {
                op_num - op_str.parse::<isize>().unwrap()
            },
            _,_,_ => {
                unreachable!()
            }
        }
    );

    assert_eq!(-40, result);
}

#[test]
fn match_commutative_non_commut_block_has_if_expr() {
    let operant1 = Operant::Str(Some("42".into()));
    let operant2 = Operant::Num(Some(2));
    let operator = Operator::Minus;

    let result = match_commutative!(
        &operant1,
        &operator,
        &operant2,
        Operant::Str(_),
        Operator::Plus,
        Operant::Num(_) => {
            unreachable!()
        }
        non_commut {
            Operant::Str(Some(op_str)),
            Operator::Minus,
            Operant::Num(Some(op_num)) if op_str.len() < 3 => {
                op_str.parse::<isize>().unwrap() - op_num
            },
            _,_,_ => {
                unreachable!()
            }
        }
    );

    assert_eq!(40, result);

    let result = match_commutative!(
        operant2,
        operator,   // operant1 and operant2 are swapped
        operant1,
        Operant::Str(_),
        Operator::Plus,
        Operant::Num(_) => {
            unreachable!()
        }
        non_commut {
            Operant::Num(Some(op_num)),
            Operator::Minus,
            Operant::Str(Some(op_str)) if op_str.len() < 3 => {
                op_num - op_str.parse::<isize>().unwrap()
            },
            Operant::Str(Some(op_str)),
            Operator::Minus,
            Operant::Num(Some(op_num)) if op_str.len() < 3 => {
                op_str.parse::<isize>().unwrap() - op_num
            },
            _,_,_ => {
                unreachable!()
            }
        }
    );

    assert_eq!(-40, result);
}

enum Operant {
    Str(Option<String>),
    Num(Option<isize>),
}

enum Operator {
    Plus,
    Mult,
    Minus,
}