//! A struct in which our data is held for rolling. By ensuring that //! only `usize`s are used we prevent the need for additional type //! checking at time of use. */ extern crate rand; use rand::Rng; use std::fmt; use std::num::ParseIntError; use super::std; #[derive(Debug, Clone, PartialEq)] pub struct Roll { die: usize, number: usize, } impl Roll { /// A fn to create new `Roll` structs, private to prevent /// being used outside the library. pub fn new(number: usize, die: usize) -> Roll { Roll { die: die, number: number, } } ///Returns the value stored in the number field of a roll. ///Allows the fields of `Roll` to be private (and unchangable) ///while still allowing access to act on their internal value /// ``` /// let x = Roll::new(&2, &6); /// assert!(x.number() == 2); /// ``` pub fn number(&self) -> usize { self.number } /// Returns the value stored in the die field of a roll. /// Allows the fields of `Roll` to be private (and unchangable) /// while still allowing access to act on their internal value pub fn die(&self) -> usize { self.die } /// Creates a Vector to hold each of the values rolled. Making /// a Vector ensures the ability to work on exactly the same /// die rolls easily and accurately, since calling the `Roll` /// iterator again will produce new results. pub fn rolls(self) -> Vec { self.collect() } /// Used on a `Roll` to return only the final value, ignoring /// the values used to add up to that result. Allows `.total()` /// syntax when used pub fn total(self) -> usize { self.sum() } /// Takes arguments and turns them into a `Roll` if possible, /// failing that it returns an error to be handled. /// Keeps taking args until less than 2 remain so it returns a /// `Vec` pub fn from_args(args: &mut std::env::Args) -> Result, ParseIntError> { //first arg is the path to the executable so skip it args.next(); let mut total: Vec = vec![]; // loop that checks that there's enough elements in args remaining // to make a `Roll` from. while args.len() >= 2 { // Collect 2 args into a `Vec` and turn them into // a `Roll` let roll: Vec = args.take(2).collect(); total.push(Roll::new( roll[0].parse::()?, roll[1].parse::()?, )); } Ok(total) } } /// Turns `Roll` into an `Iterator`, causing it to create a series of random ///numbers between 1 and the chosen die size. It counts down the number field ///of the `Roll` and returns `None` to end the iteration. It's only capable of ///returning usize values, which should never be a problem. impl Iterator for Roll { type Item = usize; fn next(&mut self) -> Option { if self.number != 0 { self.number -= 1; Some(rand::thread_rng().gen_range(1, self.die + 1)) } else { None } } } /// Defines a `Display` format for the `Roll` type. Makes it much easier ///to use the `Roll` type as you should be able to just use ///``` /// ``` impl fmt::Display for Roll { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { write!(f, "{}d{}", self.number, self.die) } } #[cfg(test)] #[test] fn rollnew() { let x = Roll::new(2, 6); let y = Roll { number: 2, die: 6 }; assert_eq!(x, y); } #[test] fn displays() { let x = Roll::new(2, 6); assert_eq!(2, x.number()); assert_eq!(6, x.die()); } #[test] fn rolls() { let x = Roll::new(20, 6); for y in x.clone().rolls() { assert!(y <= x.die()); } } #[test] fn totaling() { for _ in 0..21 { let x = Roll::new(2, 6); let y = x.total(); assert!(y <= 12 && y >= 2); let z = Roll::new(3, 20); let y = z.total(); assert!(y <= 60_usize && y >= 3_usize); } }