commit 20af27423b6203cb62770dfb55fb6a937b8feee2 Author: Beefki Date: Thu Oct 26 16:25:32 2017 -0500 Initial Commit diff --git a/Cargo.toml b/Cargo.toml new file mode 100644 index 0000000..7b596c9 --- /dev/null +++ b/Cargo.toml @@ -0,0 +1,7 @@ +[package] +name = "libroller" +version = "0.1.0" +authors = ["Beefki "] + +[dependencies] +rand = "0.3" diff --git a/src/bin/main.rs b/src/bin/main.rs new file mode 100644 index 0000000..c8fbb1e --- /dev/null +++ b/src/bin/main.rs @@ -0,0 +1,28 @@ +extern crate libroller; +use libroller::config::Config; +use std::env; + +fn main() { + let roll = Config::new(&mut env::args()); + for rolls in &roll.unwrap_or_else(|e| {println!("Application error: {}", e); + std::process::exit(1)}) + { + let x = rolls.to_roll().unwrap_or_else(|e| { println!("Application error: {}", e); + std::process::exit(1); + }); + if x.number() > 1 && x.number() < 26 { + let mut total = 0; + println!("You're rolling {}", x); + for value in x.rolls() { + println!("You rolled: {}", value); + total += value; + } + println!("Total value of dice = {}", total); + println!(""); + }else { + println!("You're rolling {}", x); + println!("Total: {}", x.total()) + } + } +} + diff --git a/src/config.rs b/src/config.rs new file mode 100644 index 0000000..9791eab --- /dev/null +++ b/src/config.rs @@ -0,0 +1,68 @@ +//! Creates a `Config` struct to be used to gather command line +//! arguments at the time the program is called, so it can be +//! converted easily to a `Roll` +use super::*; +use std::num::ParseIntError; + +#[derive(Debug)] +pub struct Config { + first: String, + second: String, +} + +impl Config { + /// Takes the arguments passed at launch and returns an Ok(_) + /// or a str with a static lifetime. This allows for error + /// handling whenever this is called. + /// + /// It checks to see if there are enough arguments passed to make + /// a proper `Config` and if not returns an `Err(&str)` that + /// explains what happened to the user. + /// + /// #Errors + /// + /// `Config::new(args)` will error when the args given don't match up with + /// the required types or when not enough args are passed to create a `Config` + pub fn new(args: &mut std::env::Args) -> Result , &'static str> { + args.next(); + let mut vec = vec![]; + + if args.len() % 2 != 0 { + return Err("Number of args not even") + } + for _ in 0..args.len()/2 { + let number = match args.next() { + None => return Err("Incorret data type entered for number of dice"), + Some(arg) => arg, + }; + let die = match args.next() { + None => return Err("Incorrect data type entered for die size"), + Some(arg) => arg, + }; + + let config = Config { + first : number, + second : die, + }; + vec.push(config) + }; + Ok(vec) + } + + /// Converts a `Config` into a `Roll`. Returns a `Result` that's + /// either `Ok(Roll)` or an `Err(_)` + pub fn to_roll(&self) -> Result { + let number = self.first.parse::()?; + let die = self.second.parse::()?; + + Ok(Roll::new(number, die)) + } +} +#[cfg(test)] +#[test] +fn roll_check() { + let x = Config { first: "2".to_string(), second: "6".to_string() }; + let x = x.to_roll().unwrap(); + let y = Roll::new(2, 6); + assert_eq!(x, y); +} diff --git a/src/lib.rs b/src/lib.rs new file mode 100644 index 0000000..952b0e9 --- /dev/null +++ b/src/lib.rs @@ -0,0 +1,7 @@ +//! A simple and efficient library to make dice rolls into easy to use values. +pub extern crate rand; +pub mod roll; +pub mod config; + +pub use roll::*; +pub use roll::Roll; diff --git a/src/roll.rs b/src/roll.rs new file mode 100644 index 0000000..e81ede4 --- /dev/null +++ b/src/roll.rs @@ -0,0 +1,113 @@ +//! A struct in which our data is held for rolling. By ensuring that +//! only `u32`s are used we prevent the need for additional type +//! checking at time of use. */ +extern crate rand; +use rand::Rng; +use std::fmt; + +#[derive(Debug, Clone, PartialEq)] +pub struct Roll { + die: u32, + number: u32, +} +impl Roll { + /// A fn to create new `Roll` structs, private to prevent + /// being used outside the library. + pub fn new(number: u32, die: u32) -> 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) -> u32 { + 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) -> u32 { + 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) -> u32 { + self.sum() + } +} + +/// 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 u32 values, which should never be a problem. +impl Iterator for Roll { + type Item = u32; + + 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.rolls() { + assert!(y <= x.die()); + } +} +#[test] +fn totaling() { + for _ in 0 .. 21 { + let x = Roll::new(2, 6); + assert!(x.total() <= 12 && x.total() >= 2); + let y = Roll::new(3, 20); + assert!(y.total() <= 60 && y.total() >= 3); + } +} +