openpql-range-parser 0.1.0

Poker Range Notation Parser
Documentation
use super::{
    CardRank, CardSuit, Display, From, RankConst, RankVar, SuitConst, SuitVar,
};

#[derive(Copy, Clone, PartialEq, Eq, Debug, Display)]
pub enum RangeCard {
    #[display("{_0}{_1}")]
    CC(RankConst, SuitConst),
    #[display("{_0}{_1}")]
    CV(RankConst, SuitVar),
    #[display("{_0}")]
    CA(RankConst),
    #[display("{_0}{_1}")]
    VC(RankVar, SuitConst),
    #[display("{_0}{_1}")]
    VV(RankVar, SuitVar),
    #[display("{_0}")]
    VA(RankVar),
    #[display("{_0}")]
    AC(SuitConst),
    #[display("{_0}")]
    AV(SuitVar),
    #[display("*")]
    AA,
}

impl From<(CardRank, CardSuit)> for RangeCard {
    fn from(t: (CardRank, CardSuit)) -> Self {
        match t {
            (CardRank::Const(r), CardSuit::Const(s)) => Self::CC(r, s),
            (CardRank::Const(r), CardSuit::Var(s)) => Self::CV(r, s),
            (CardRank::Var(r), CardSuit::Const(s)) => Self::VC(r, s),
            (CardRank::Var(r), CardSuit::Var(s)) => Self::VV(r, s),
            (CardRank::Any, CardSuit::Const(s)) => Self::AC(s),
            (CardRank::Any, CardSuit::Var(s)) => Self::AV(s),
        }
    }
}

impl From<CardRank> for RangeCard {
    fn from(r: CardRank) -> Self {
        match r {
            CardRank::Const(c) => Self::CA(c),
            CardRank::Var(v) => Self::VA(v),
            CardRank::Any => Self::AA,
        }
    }
}

impl From<CardSuit> for RangeCard {
    fn from(s: CardSuit) -> Self {
        match s {
            CardSuit::Const(c) => Self::AC(c),
            CardSuit::Var(v) => Self::AV(v),
        }
    }
}

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

    impl Arbitrary for RangeCard {
        fn arbitrary(g: &mut quickcheck::Gen) -> Self {
            match *g.choose(&[0, 1, 2, 3, 4, 5, 6, 7, 8]).unwrap() {
                0 => Self::CC(RankConst::arbitrary(g), SuitConst::arbitrary(g)),
                1 => Self::CV(RankConst::arbitrary(g), SuitVar::arbitrary(g)),
                2 => Self::CA(RankConst::arbitrary(g)),
                3 => Self::VC(RankVar::arbitrary(g), SuitConst::arbitrary(g)),
                4 => Self::VV(RankVar::arbitrary(g), SuitVar::arbitrary(g)),
                5 => Self::VA(RankVar::arbitrary(g)),
                6 => Self::AC(SuitConst::arbitrary(g)),
                7 => Self::AV(SuitVar::arbitrary(g)),
                _ => Self::AA,
            }
        }
    }

    #[test]
    fn test_card_cc() {
        assert_range_card("As", "As");
    }

    #[test]
    fn test_card_cv() {
        assert_range_card("Aw", "Aw");
    }

    #[test]
    fn test_card_ca() {
        assert_range_card("A", "A");
    }

    #[test]
    fn test_card_vc() {
        assert_range_card("Bs", "Bs");
    }

    #[test]
    fn test_card_vv() {
        assert_range_card("Bw", "Bw");
    }

    #[test]
    fn test_card_va() {
        assert_range_card("B", "B");
    }

    #[test]
    fn test_card_ac() {
        assert_range_card("s", "s");
        assert_range_card("*s", "s");
    }

    #[test]
    fn test_card_av() {
        assert_range_card("w", "w");
        assert_range_card("*w", "w");
    }

    #[test]
    fn test_card_aa() {
        assert_range_card("*", "*");
    }

    #[quickcheck]
    fn test_shortdeck(card: RangeCard) {
        let src = card.to_string();
        if src.chars().any(|c| ['2', '3', '4', '5'].contains(&c)) {
            assert!(parse_card_sd(&src).is_err());
        } else {
            assert_eq!(Ok(card), parse_card_sd(&src));
        }
    }

    fn assert_range_card_sd_err(s: &str, expected: Error) {
        assert_eq!(parse_card_sd(s), Err(expected.into()));
    }

    #[test]
    fn test_shortdeck_error() {
        assert_range_card_sd_err("2", Error::InvalidRank((0, 1)));
        assert_range_card_sd_err("3", Error::InvalidRank((0, 1)));
        assert_range_card_sd_err("4", Error::InvalidRank((0, 1)));
        assert_range_card_sd_err("5", Error::InvalidRank((0, 1)));
    }
}