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
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
use regex::Regex;
use std::error;
use std::fmt;
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
#[derive(Copy, Clone, PartialEq, Eq, Debug)]
pub struct DiceType {
pub n_dice: i32,
pub die_type: i32,
pub bonus: i32,
}
impl DiceType {
pub fn new(n_dice: i32, die_type: i32, bonus: i32) -> Self {
DiceType {
n_dice,
die_type,
bonus,
}
}
}
impl Default for DiceType {
fn default() -> DiceType {
DiceType {
n_dice: 1,
die_type: 4,
bonus: 0,
}
}
}
#[derive(Debug, Clone)]
pub struct DiceParseError;
impl std::fmt::Display for DiceParseError {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "Invalid dice string")
}
}
impl error::Error for DiceParseError {
fn source(&self) -> Option<&(dyn error::Error + 'static)> {
None
}
}
#[allow(dead_code)]
#[cfg(feature = "parsing")]
pub fn parse_dice_string(dice: &str) -> Result<DiceType, DiceParseError> {
lazy_static! {
static ref DICE_RE: Regex = Regex::new(r"(\d+)d(\d+)([\+\-]\d+)?").unwrap();
}
let mut result: DiceType = DiceType::default();
let mut did_something = false;
for cap in DICE_RE.captures_iter(dice) {
did_something = true;
if let Some(group) = cap.get(1) {
match group.as_str().parse::<i32>() {
Ok(number) => result.n_dice = number,
Err(_) => return Err(DiceParseError {}),
}
} else {
return Err(DiceParseError {});
}
if let Some(group) = cap.get(2) {
match group.as_str().parse::<i32>() {
Ok(number) => result.die_type = number,
Err(_) => return Err(DiceParseError {}),
}
} else {
return Err(DiceParseError {});
}
if let Some(group) = cap.get(3) {
match group.as_str().parse::<i32>() {
Ok(number) => result.bonus = number,
Err(_) => return Err(DiceParseError {}),
}
}
}
if !did_something {
return Err(DiceParseError {});
}
Ok(result)
}
#[cfg(test)]
mod tests {
use super::{parse_dice_string, DiceType};
#[test]
fn parse_1d6() {
assert_eq!(parse_dice_string("1d6").unwrap(), DiceType::new(1, 6, 0));
}
#[test]
fn parse_1d20plus4() {
assert_eq!(
parse_dice_string("1d20+4").unwrap(),
DiceType::new(1, 20, 4)
);
}
#[test]
fn parse_3d6minus2() {
assert_eq!(parse_dice_string("3d6-2").unwrap(), DiceType::new(3, 6, -2));
}
#[test]
fn fail_parsing() {
assert!(parse_dice_string("blah").is_err());
}
}