1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
pub const ACE: Rank = 1;
pub const JACK: Rank = 11;
pub const QUEEN: Rank = 12;
pub const KING: Rank = 13;

/// Indicates the rank of a card.
///
/// The ranks are as follows:
/// - An Ace has rank 1
/// - Numbered cards have the rank corresponding to their numbers
/// - A Jack has rank 11
/// - A Queen has rank 12
/// - A King has rank 13
///
/// Rank 0 and ranks greater than 13 are not valid card ranks.
pub type Rank = u8;

/// Converts a String to a Rank.
///
/// The string should be either the rank as number between 1 and 13 or one of these strings that
/// represent the named cards:
/// "Ace", "A", "Jack", "J", "Queen", "Q", "King", "K" or "T", which represents 10.
/// The strings are case-insensitive.
///
/// # Examples
///
/// ```
/// use freecell::{rank_from_string, ACE, JACK, KING};
///
/// assert_eq!(Ok(ACE), rank_from_string("ace"));
/// assert_eq!(Ok(JACK), rank_from_string("J"));
/// assert_eq!(Ok(10), rank_from_string("t"));
/// assert_eq!(Ok(8), rank_from_string("8"));
/// assert_eq!(Ok(KING), rank_from_string("13"));
/// assert!(rank_from_string("0").is_err());
/// assert!(rank_from_string("14").is_err());
/// ```
pub fn rank_from_string<S: Into<String>>(string: S) -> Result<Rank, String> {
    let string = string.into();
    match string.to_lowercase().trim() {
        "ace" => Ok(ACE),
        "a" => Ok(ACE),
        "t" => Ok(10),
        "jack" => Ok(JACK),
        "j" => Ok(JACK),
        "queen" => Ok(QUEEN),
        "q" => Ok(QUEEN),
        "king" => Ok(KING),
        "k" => Ok(KING),

        // rank is not a word => try to parse it as u8
        _ => match string.trim().parse::<u8>() {
            Err(_) => Err(format!("Rank is neither named rank nor integer: \"{}\"", string)),

            // successfully parsed number => check whether it is in the correct range
            Ok(rank) => match rank {
                0 => Err("Rank cannot be 0".to_string()),
                1..=13 => Ok(rank),
                _ => Err(format!("Rank cannot be greater than 13 (rank is {})", rank)),
            },
        },
    }
}