tudelft_dice_example/
lib.rs1#![deny(warnings)]
2#![deny(missing_docs)]
3use rand::prelude::*;
6use std::num::ParseIntError;
7use thiserror::Error;
8
9#[derive(Debug, PartialEq)]
10struct DiceRoll {
11 number: usize,
12 sides: usize,
13}
14
15#[derive(Debug, PartialEq, Eq, Error)]
17pub enum RollError {
18 #[error("syntax incorrect")]
20 IncorrectSyntax,
21 #[error("couldn't parse int")]
23 ParseNumber(#[from] ParseIntError),
24}
25
26fn parse(s: &str) -> Result<DiceRoll, RollError> {
27 let parts = s.split('d').collect::<Vec<_>>();
28
29 if parts.len() != 2 {
30 return Err(RollError::IncorrectSyntax);
31 }
32
33 Ok(DiceRoll {
34 number: parts[0].parse()?,
35 sides: parts[1].parse()?,
36 })
37}
38
39fn throw_dice(s: &DiceRoll) -> Vec<usize> {
40 let mut rng = rand::thread_rng();
41
42 (0..s.number).map(|_| rng.gen_range(1..=s.sides)).collect()
43}
44
45pub fn dice(s: &str) -> Result<Vec<usize>, RollError> {
49 let d = parse(s)?;
50
51 Ok(throw_dice(&d))
52}
53
54
55#[cfg(test)]
56mod tests {
57 use crate::{dice, parse, DiceRoll, RollError};
58
59
60 #[test]
61 fn smoke() {
62 assert!(dice("1d6").unwrap()[0] < 7);
63 }
64
65 #[test]
66 fn test_6d1() {
67 assert_eq!(dice("6d1").unwrap(), vec![1; 6], "should be all ones {}", 1);
68 }
69
70 #[test]
71 fn test_parse_1d6() {
72 assert_eq!(parse("1d6"), Ok(DiceRoll { number: 1, sides: 6 }));
73 assert_eq!(parse("6d6"), Ok(DiceRoll { number: 6, sides: 6 }));
74 assert_eq!(parse("1d12"), Ok(DiceRoll { number: 1, sides: 12 }));
75 assert_eq!(parse("1b12"), Err(RollError::IncorrectSyntax));
76 assert!(matches!(
77 dice("xd12"),
78 Err(RollError::ParseNumber(_))
79 ));
80 assert!(matches!(
81 dice("12dx"),
82 Err(RollError::ParseNumber(_))
83 ));
84 }
85}
86