bracket_random/
parsing.rs1use regex::Regex;
2use std::error;
3use std::fmt;
4
5#[cfg(feature = "serde")]
6use serde_crate::{Deserialize, Serialize};
7
8#[cfg_attr(
10 feature = "serde",
11 derive(Serialize, Deserialize),
12 serde(crate = "serde_crate")
13)]
14#[derive(Copy, Clone, PartialEq, Eq, Debug)]
15pub struct DiceType {
16 pub n_dice: i32,
17 pub die_type: i32,
18 pub bonus: i32,
19}
20
21impl DiceType {
22 pub fn new(n_dice: i32, die_type: i32, bonus: i32) -> Self {
23 DiceType {
24 n_dice,
25 die_type,
26 bonus,
27 }
28 }
29}
30
31impl Default for DiceType {
32 fn default() -> DiceType {
33 DiceType {
34 n_dice: 1,
35 die_type: 4,
36 bonus: 0,
37 }
38 }
39}
40
41#[derive(Debug, Clone)]
42pub struct DiceParseError;
43
44impl std::fmt::Display for DiceParseError {
45 fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
46 write!(f, "Invalid dice string")
47 }
48}
49
50impl error::Error for DiceParseError {
51 fn source(&self) -> Option<&(dyn error::Error + 'static)> {
52 None
54 }
55}
56
57#[allow(dead_code)]
58#[cfg(feature = "parsing")]
60pub fn parse_dice_string(dice: &str) -> Result<DiceType, DiceParseError> {
61 let dice = &dice.split_whitespace().collect::<Vec<_>>().join("");
62 lazy_static! {
63 static ref DICE_RE: Regex = Regex::new(r"(\d+)d(\d+)([\+\-]\d+)?").unwrap();
64 }
65 let mut result: DiceType = DiceType::default();
66 let mut did_something = false;
67 for cap in DICE_RE.captures_iter(dice) {
68 did_something = true;
69 if let Some(group) = cap.get(1) {
70 match group.as_str().parse::<i32>() {
71 Ok(number) => result.n_dice = number,
72 Err(_) => return Err(DiceParseError {}),
73 }
74 } else {
75 return Err(DiceParseError {});
76 }
77 if let Some(group) = cap.get(2) {
78 match group.as_str().parse::<i32>() {
79 Ok(number) => result.die_type = number,
80 Err(_) => return Err(DiceParseError {}),
81 }
82 } else {
83 return Err(DiceParseError {});
84 }
85 if let Some(group) = cap.get(3) {
86 match group.as_str().parse::<i32>() {
87 Ok(number) => result.bonus = number,
88 Err(_) => return Err(DiceParseError {}),
89 }
90 }
91 }
92 if !did_something {
93 return Err(DiceParseError {});
94 }
95 Ok(result)
96}
97
98#[cfg(test)]
99mod tests {
100 use super::{parse_dice_string, DiceType};
101
102 #[test]
103 fn parse_1d6() {
104 assert_eq!(parse_dice_string("1d6").unwrap(), DiceType::new(1, 6, 0));
105 }
106
107 #[test]
108 fn parse_1d20plus4() {
109 assert_eq!(
110 parse_dice_string("1d20+4").unwrap(),
111 DiceType::new(1, 20, 4)
112 );
113 }
114
115 #[test]
116 fn parse_3d6minus2() {
117 assert_eq!(parse_dice_string("3d6-2").unwrap(), DiceType::new(3, 6, -2));
118 }
119
120 #[test]
121 fn parse_whitespace_test() {
122 assert_eq!(
123 parse_dice_string("3d6 - 2").unwrap(),
124 DiceType::new(3, 6, -2)
125 );
126 assert_eq!(
127 parse_dice_string(" 3d6- 2").unwrap(),
128 DiceType::new(3, 6, -2)
129 );
130 assert_eq!(
131 parse_dice_string("3 d 6- 2").unwrap(),
132 DiceType::new(3, 6, -2)
133 );
134 }
135
136 #[test]
137 fn fail_parsing() {
138 assert!(parse_dice_string("blah").is_err());
139 }
140
141 #[cfg(feature = "serde")]
142 #[test]
143 fn serialize_parsing() {
144 use serde_crate::{Deserialize, Serialize};
145 let d = parse_dice_string("3d6 - 2").unwrap();
146 let serialized = serde_json::to_string(&d).unwrap();
147 let deserialized: DiceType = serde_json::from_str(&serialized).unwrap();
148 assert!(d == deserialized);
149 }
150}