From fdb771daec8840907257d4a341c52f213aa4d552 Mon Sep 17 00:00:00 2001 From: Jasper <40232406+jaspwr@users.noreply.github.com> Date: Mon, 22 May 2023 18:06:28 +1000 Subject: [PATCH 1/5] Improve errors --- Cargo.toml | 1 + src/lib.rs | 53 +++++++++++++++++++++++++++------------------------- src/tests.rs | 30 ++++++++++++++--------------- 3 files changed, 44 insertions(+), 40 deletions(-) diff --git a/Cargo.toml b/Cargo.toml index 98d412b..172cc61 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,7 @@ [package] name = "d20" version = "0.1.0" +edition = "2021" authors = ["Dan Nemeth ", "Kevin Hoffman "] description = "A library for rolling dice based on simple expressions" license = "MPL-2.0" diff --git a/src/lib.rs b/src/lib.rs index bd0808c..4c92bd1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,10 +1,10 @@ //! D20 //! //! **D20** is a simple crate designed to evaluate _roll expressions_. A _roll expression_ is an -//! english-language string that reflects the intent of a dungeon or game master to perform a +//! english-language string that reflects the intent of a dungeon or game master to perform a //! particular roll. //! -//! For example, in a tabletop game you may frequently hear phrases like _"roll 2d10"_, or +//! For example, in a tabletop game you may frequently hear phrases like _"roll 2d10"_, or //! _"roll 3d6 and add 5"_. These are roll expressions, and the components within them are //! what we call _die roll terms_. A _die roll term_ is either a term that calls for the rolling //! of an n-sided die x times (e.g. 3d6) or a modifier that simply adds or subtracts a constant value @@ -45,7 +45,7 @@ //! rolls of the given die roll expression. //! //! _Note that it will be necessary to constrain the iterator via `take(n)`._ -//! +//! //! ```rust //! extern crate d20; //! use d20::*; @@ -56,7 +56,7 @@ //! assert_eq!(v.len(), 3); //! assert!(v[0].total >= 3 && v[0].total <= 18); //! assert!(v[1].total >= 3 && v[1].total <= 18); -//! assert!(v[2].total >= 3 && v[2].total <= 18); +//! assert!(v[2].total >= 3 && v[2].total <= 18); //! } //! //! ``` @@ -73,22 +73,22 @@ //! # } //! ``` //! -//! +//! extern crate rand; extern crate regex; -use std::fmt; +use std::{fmt, error::Error}; use rand::{thread_rng, Rng}; use regex::Regex; -/// Represents the _results_ of an evaluated die roll expression. -/// +/// Represents the _results_ of an evaluated die roll expression. +/// /// The `Roll` struct contains the original _die roll expression_ passed to the `roll_dice()` /// function. /// -/// The list of `values` will always be a vector containing at least one element because roll +/// The list of `values` will always be a vector containing at least one element because roll /// expressions are not valid without at least 1 term. Each resulting value is a tuple containing /// the parsed `DieRollTerm` and a vector of values. For `DieRollTerm::Modifier` terms, this will be a single-element /// vector containing the modifier value. For `DieRollTerm::DieRoll` terms, this will be a vector @@ -108,14 +108,14 @@ pub struct Roll { } -/// Formats roll results, including die rolls, in a human-readable string. +/// Formats roll results, including die rolls, in a human-readable string. /// /// For example, if the original expression was `3d6+5`, formatting the `Roll` struct /// might result in the following text: /// /// `3d6[3,4,6]+5 (Total: 18)` impl fmt::Display for Roll { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut out = String::new(); for i in 0..self.values.len() { @@ -133,7 +133,7 @@ impl fmt::Display for Roll { } /// Converts an evaluated roll expression into an iterator, allowing the expression -/// to be evaluated (including re-rolling of dice) multiple times. +/// to be evaluated (including re-rolling of dice) multiple times. impl IntoIterator for Roll { type Item = Roll; type IntoIter = RollIterator; @@ -184,15 +184,15 @@ pub enum DieRollTerm { impl DieRollTerm { - fn parse(drt: &str) -> DieRollTerm { + fn parse(drt: &str) -> Result> { if drt.to_lowercase().contains('d') { let v: Vec<&str> = drt.split("d").collect(); - DieRollTerm::DieRoll { - multiplier: v[0].parse::().unwrap(), - sides: v[1].parse::().unwrap(), - } + Ok(DieRollTerm::DieRoll { + multiplier: v[0].parse::()?, + sides: v[1].parse::()?, + }) } else { - DieRollTerm::Modifier(drt.parse::().unwrap()) + Ok(DieRollTerm::Modifier(drt.parse::()?)) } } @@ -222,7 +222,7 @@ impl DieRollTerm { /// Formats an individual die roll term in a human-friendly fashion. For `Modifier` terms, /// this will force the printing of a + or - sign before the modifier value. For `DieRoll` -/// terms, this displays the term in the form `5d10`. +/// terms, this displays the term in the form `5d10`. impl fmt::Display for DieRollTerm { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { @@ -237,7 +237,10 @@ impl fmt::Display for DieRollTerm { /// text indicating why the function was unable to roll the dice / evaluate the expression. pub fn roll_dice<'a>(s: &'a str) -> Result { let s: String = s.split_whitespace().collect(); - let terms: Vec = parse_die_roll_terms(&s); + let terms: Vec = match parse_die_roll_terms(&s) { + Ok(t) => t, + Err(_) => return Err("Invalid die roll expression: unable to parse terms."), + }; if terms.len() == 0 { Err("Invalid die roll expression: no die roll terms found.") @@ -254,20 +257,20 @@ pub fn roll_dice<'a>(s: &'a str) -> Result { } } -fn parse_die_roll_terms(drex: &str) -> Vec { +fn parse_die_roll_terms(drex: &str) -> Result, Box> { let mut terms = Vec::new(); - let re = Regex::new(r"([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)").unwrap(); + let re = Regex::new(r"([+-]?\s*\d+[dD]\d+|[+-]?\s*\d+)")?; let matches = re.find_iter(drex); for m in matches { - terms.push(DieRollTerm::parse(&drex[m.start()..m.end()])); + terms.push(DieRollTerm::parse(&drex[m.start()..m.end()])?); } - terms + Ok(terms) } /// Generates a random number within the specified range. Returns a `Result` containing -/// either a valid signed 32-bit integer with the randomly generated number or some text +/// either a valid signed 32-bit integer with the randomly generated number or some text /// indicating the reason for failure. pub fn roll_range<'a>(min: i32, max: i32) -> Result { if min > max { diff --git a/src/tests.rs b/src/tests.rs index e7557f3..37c8fe9 100644 --- a/src/tests.rs +++ b/src/tests.rs @@ -9,7 +9,7 @@ fn die_roll_expression_parsed() { let nd = "-4d10+5".to_string(); let mf = "50+2d8-1d4".to_string(); - let pv = parse_die_roll_terms(&pd); + let pv = parse_die_roll_terms(&pd).unwrap(); if let DieRollTerm::DieRoll { multiplier: m, sides: s } = pv[0] { assert_eq!(m, 3); assert_eq!(s, 12); @@ -18,7 +18,7 @@ fn die_roll_expression_parsed() { assert_eq!(n, 4); } - let nv = parse_die_roll_terms(&nd); + let nv = parse_die_roll_terms(&nd).unwrap(); if let DieRollTerm::DieRoll { multiplier: m, sides: s } = nv[0] { assert_eq!(m, -4); assert_eq!(s, 10); @@ -27,7 +27,7 @@ fn die_roll_expression_parsed() { assert_eq!(n, 5); } - let mv = parse_die_roll_terms(&mf); + let mv = parse_die_roll_terms(&mf).unwrap(); if let DieRollTerm::Modifier(n) = mv[0] { assert_eq!(n, 50); } @@ -46,8 +46,8 @@ fn die_roll_expression_parsed() { fn die_roll_term_parsed() { let drt = "3d6".to_string(); let mfy = "+7".to_string(); - let drt = DieRollTerm::parse(&drt); - let mfy = DieRollTerm::parse(&mfy); + let drt = DieRollTerm::parse(&drt).unwrap(); + let mfy = DieRollTerm::parse(&mfy).unwrap(); if let DieRollTerm::DieRoll { multiplier: m, sides: s } = drt { assert_eq!(m, 3); assert_eq!(s, 6); @@ -64,10 +64,10 @@ fn die_roll_term_parsed() { #[test] fn die_roll_term_calculated() { - let dt = DieRollTerm::parse("6d1").evaluate(); - let nt = DieRollTerm::parse("-4d1").evaluate(); - let pm = DieRollTerm::parse("+7").evaluate(); - let nm = DieRollTerm::parse("-7").evaluate(); + let dt = DieRollTerm::parse("6d1").unwrap().evaluate(); + let nt = DieRollTerm::parse("-4d1").unwrap().evaluate(); + let pm = DieRollTerm::parse("+7").unwrap().evaluate(); + let nm = DieRollTerm::parse("-7").unwrap().evaluate(); let dtr = DieRollTerm::calculate(dt); assert_eq!(dtr, 6); @@ -84,7 +84,7 @@ fn die_roll_term_calculated() { #[test] fn die_roll_term_evaluated() { - let drt = DieRollTerm::parse("3d1"); + let drt = DieRollTerm::parse("3d1").unwrap(); let v = drt.evaluate(); assert_eq!(v.1.len(), 3); @@ -95,8 +95,8 @@ fn die_roll_term_evaluated() { #[test] fn die_roll_term_modifier_evaluated() { - let mfy = DieRollTerm::parse("+7"); - let mfy2 = DieRollTerm::parse("-7"); + let mfy = DieRollTerm::parse("+7").unwrap(); + let mfy2 = DieRollTerm::parse("-7").unwrap(); let v1 = mfy.evaluate(); let v2 = mfy2.evaluate(); @@ -172,9 +172,9 @@ fn iterator_yields_new_results() { #[test] fn die_roll_term_displays_properly() { - let drt = DieRollTerm::parse("3d6"); - let pm = DieRollTerm::parse("5"); - let nm = DieRollTerm::parse("-6"); + let drt = DieRollTerm::parse("3d6").unwrap(); + let pm = DieRollTerm::parse("5").unwrap(); + let nm = DieRollTerm::parse("-6").unwrap(); let out = format!("{}", drt); assert_eq!(out, "3d6"); From d6c57e10e4add69f8ab53b0e454f5b0407b03b2a Mon Sep 17 00:00:00 2001 From: Jasper <40232406+jaspwr@users.noreply.github.com> Date: Mon, 22 May 2023 18:07:40 +1000 Subject: [PATCH 2/5] Fix edition --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 172cc61..7dd14a0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,7 +1,7 @@ [package] name = "d20" version = "0.1.0" -edition = "2021" +edition = "2015" authors = ["Dan Nemeth ", "Kevin Hoffman "] description = "A library for rolling dice based on simple expressions" license = "MPL-2.0" From a07ba598a59cbba018757f8108ce330030e66c28 Mon Sep 17 00:00:00 2001 From: Jasper <40232406+jaspwr@users.noreply.github.com> Date: Mon, 22 May 2023 18:09:08 +1000 Subject: [PATCH 3/5] Version bump --- Cargo.toml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Cargo.toml b/Cargo.toml index 7dd14a0..3f4f333 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "d20" -version = "0.1.0" +version = "0.1.1" edition = "2015" authors = ["Dan Nemeth ", "Kevin Hoffman "] description = "A library for rolling dice based on simple expressions" From e3a75b584c5bc66645f2ba332a004ebb57d07de2 Mon Sep 17 00:00:00 2001 From: Jasper <40232406+jaspwr@users.noreply.github.com> Date: Mon, 22 May 2023 18:24:47 +1000 Subject: [PATCH 4/5] Formatting fix --- src/lib.rs | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index 4c92bd1..ee59be6 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -2,9 +2,9 @@ //! //! **D20** is a simple crate designed to evaluate _roll expressions_. A _roll expression_ is an //! english-language string that reflects the intent of a dungeon or game master to perform a -//! particular roll. +//! particular roll. //! -//! For example, in a tabletop game you may frequently hear phrases like _"roll 2d10"_, or +//! For example, in a tabletop game you may frequently hear phrases like _"roll 2d10"_, or //! _"roll 3d6 and add 5"_. These are roll expressions, and the components within them are //! what we call _die roll terms_. A _die roll term_ is either a term that calls for the rolling //! of an n-sided die x times (e.g. 3d6) or a modifier that simply adds or subtracts a constant value @@ -45,7 +45,7 @@ //! rolls of the given die roll expression. //! //! _Note that it will be necessary to constrain the iterator via `take(n)`._ -//! +//! //! ```rust //! extern crate d20; //! use d20::*; @@ -56,7 +56,7 @@ //! assert_eq!(v.len(), 3); //! assert!(v[0].total >= 3 && v[0].total <= 18); //! assert!(v[1].total >= 3 && v[1].total <= 18); -//! assert!(v[2].total >= 3 && v[2].total <= 18); +//! assert!(v[2].total >= 3 && v[2].total <= 18); //! } //! //! ``` @@ -73,7 +73,7 @@ //! # } //! ``` //! -//! +//! extern crate rand; extern crate regex; @@ -83,12 +83,12 @@ use regex::Regex; -/// Represents the _results_ of an evaluated die roll expression. -/// +/// Represents the _results_ of an evaluated die roll expression. +/// /// The `Roll` struct contains the original _die roll expression_ passed to the `roll_dice()` /// function. /// -/// The list of `values` will always be a vector containing at least one element because roll +/// The list of `values` will always be a vector containing at least one element because roll /// expressions are not valid without at least 1 term. Each resulting value is a tuple containing /// the parsed `DieRollTerm` and a vector of values. For `DieRollTerm::Modifier` terms, this will be a single-element /// vector containing the modifier value. For `DieRollTerm::DieRoll` terms, this will be a vector @@ -108,14 +108,14 @@ pub struct Roll { } -/// Formats roll results, including die rolls, in a human-readable string. +/// Formats roll results, including die rolls, in a human-readable string. /// /// For example, if the original expression was `3d6+5`, formatting the `Roll` struct /// might result in the following text: /// /// `3d6[3,4,6]+5 (Total: 18)` impl fmt::Display for Roll { - fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { let mut out = String::new(); for i in 0..self.values.len() { @@ -133,7 +133,7 @@ impl fmt::Display for Roll { } /// Converts an evaluated roll expression into an iterator, allowing the expression -/// to be evaluated (including re-rolling of dice) multiple times. +/// to be evaluated (including re-rolling of dice) multiple times. impl IntoIterator for Roll { type Item = Roll; type IntoIter = RollIterator; @@ -222,7 +222,7 @@ impl DieRollTerm { /// Formats an individual die roll term in a human-friendly fashion. For `Modifier` terms, /// this will force the printing of a + or - sign before the modifier value. For `DieRoll` -/// terms, this displays the term in the form `5d10`. +/// terms, this displays the term in the form `5d10`. impl fmt::Display for DieRollTerm { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { match *self { From 36ef44d7fbf75f3bbfe7f51b0e55b533a5dfaa34 Mon Sep 17 00:00:00 2001 From: Jasper <40232406+jaspwr@users.noreply.github.com> Date: Mon, 22 May 2023 18:26:31 +1000 Subject: [PATCH 5/5] Formatting fix --- src/lib.rs | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/lib.rs b/src/lib.rs index ee59be6..42ae303 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,8 +1,8 @@ //! D20 //! //! **D20** is a simple crate designed to evaluate _roll expressions_. A _roll expression_ is an -//! english-language string that reflects the intent of a dungeon or game master to perform a -//! particular roll. +//! english-language string that reflects the intent of a dungeon or game master to perform a +//! particular roll. //! //! For example, in a tabletop game you may frequently hear phrases like _"roll 2d10"_, or //! _"roll 3d6 and add 5"_. These are roll expressions, and the components within them are @@ -270,7 +270,7 @@ fn parse_die_roll_terms(drex: &str) -> Result, Box> } /// Generates a random number within the specified range. Returns a `Result` containing -/// either a valid signed 32-bit integer with the randomly generated number or some text +/// either a valid signed 32-bit integer with the randomly generated number or some text /// indicating the reason for failure. pub fn roll_range<'a>(min: i32, max: i32) -> Result { if min > max {