Added segment and filter enums and constructors and added keep and
lowest/highest options for keep/drop
This commit is contained in:
parent
565e97905a
commit
015aeeac61
72
src/lib.rs
72
src/lib.rs
@ -1,9 +1,74 @@
|
||||
use regex::Regex;
|
||||
use thiserror::Error;
|
||||
use anyhow::Result;
|
||||
|
||||
#[cfg(test)]
|
||||
mod lib_test;
|
||||
|
||||
fn parse_dice_segments<'a>(cmd: &'a str) -> Vec<regex::Captures<'a>> {
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum DiceFilter {
|
||||
DropLowest(i32), DropHighest(i32), KeepLowest(i32), KeepHighest(i32),
|
||||
}
|
||||
|
||||
#[derive(Debug, PartialEq)]
|
||||
enum Segment {
|
||||
DiceRoll{
|
||||
op: char,
|
||||
count: i32,
|
||||
size: i32,
|
||||
filter: Option<DiceFilter>,
|
||||
},
|
||||
Modifier{
|
||||
op: char,
|
||||
amount: i32,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Error, Debug, PartialEq)]
|
||||
enum SegmentError {
|
||||
#[error("Invalid segment")]
|
||||
InvalidSegment,
|
||||
#[error("No dice size was given")]
|
||||
SizeIsMissing,
|
||||
#[error("Invalid filter operator: {op}")]
|
||||
InvalidFilterOperator{op: String},
|
||||
#[error("Incomplete filter")]
|
||||
IncompleteFilter,
|
||||
}
|
||||
|
||||
fn construct_dice_filter(op: &str, amount: i32) -> Result<DiceFilter> {
|
||||
match op.to_lowercase().as_str() {
|
||||
"d" | "dl" => Ok(DiceFilter::DropLowest(amount)),
|
||||
"dh" => Ok(DiceFilter::DropHighest(amount)),
|
||||
"k" | "kh" => Ok(DiceFilter::KeepHighest(amount)),
|
||||
"kl" => Ok(DiceFilter::KeepLowest(amount)),
|
||||
_ => Err(SegmentError::InvalidFilterOperator{op: op.to_owned()})?,
|
||||
}
|
||||
}
|
||||
|
||||
fn construct_dice_segment(cap: regex::Captures) -> Result<Segment> {
|
||||
let op = cap.name("op").and_then(|i| i.as_str().chars().next()).unwrap_or('+');
|
||||
let modifier = cap.name("mod").map(|i| i.as_str().parse()).transpose()?;
|
||||
|
||||
if let Some(amount) = modifier {
|
||||
return Ok(Segment::Modifier{ op, amount });
|
||||
}
|
||||
|
||||
let count:i32 = cap.name("count").map(|i| i.as_str().parse()).transpose()?.unwrap_or(1);
|
||||
let size:i32 = cap.name("size").map(|i| i.as_str().parse()).transpose()?.ok_or(SegmentError::SizeIsMissing)?;
|
||||
let filter_amount = cap.name("filter").map(|i| i.as_str().parse::<i32>()).transpose()?;
|
||||
let filter_op = cap.name("filter_op").map(|op| op.as_str());
|
||||
dbg!(filter_amount);
|
||||
dbg!(filter_op);
|
||||
if filter_amount.is_some() != filter_op.is_some() {
|
||||
Err(SegmentError::IncompleteFilter)?;
|
||||
}
|
||||
let filter = filter_amount.and_then(|amount| (filter_op.map(|op| construct_dice_filter(op, amount)))).transpose()?;
|
||||
|
||||
Ok(Segment::DiceRoll{op, count, size, filter})
|
||||
}
|
||||
|
||||
fn parse_dice_segments(cmd: &str) -> Result<Vec<Segment>> {
|
||||
let regex = Regex::new(r#"(?x)
|
||||
(?P<op>[+\-/*])?
|
||||
\s*
|
||||
@ -12,12 +77,13 @@ fn parse_dice_segments<'a>(cmd: &'a str) -> Vec<regex::Captures<'a>> {
|
||||
(?P<count>\d+)? # count (optional)
|
||||
d
|
||||
(?P<size>\d+) # dice size
|
||||
(?:d(?P<drop>\d+))?
|
||||
(?P<filter_op>[dk][hl]?)?
|
||||
(?P<filter>\d+)?
|
||||
) | (?:
|
||||
(?P<mod>\d+)
|
||||
)
|
||||
)
|
||||
"#).expect("Failed to compile regex");
|
||||
|
||||
regex.captures_iter(cmd).collect()
|
||||
regex.captures_iter(cmd).map(construct_dice_segment).collect()
|
||||
}
|
||||
|
||||
@ -2,27 +2,35 @@ use super::*;
|
||||
|
||||
#[test]
|
||||
fn test_roll_dice() {
|
||||
let mut results = parse_dice_segments("2d6");
|
||||
let mut results = parse_dice_segments("2d6").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0]["count"], *"2");
|
||||
assert_eq!(results[0]["size"], *"6");
|
||||
assert_eq!(results, vec![ Segment::DiceRoll{ op: '+', count: 2, size: 6, filter: None } ]);
|
||||
|
||||
results = parse_dice_segments("2d6 + 1d20");
|
||||
results = parse_dice_segments("2d6 + 1d20").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results.len(), 2);
|
||||
assert_eq!(results[1].name("op").map(|i| i.as_str()), Some("+"));
|
||||
assert_eq!(results[1].name("count").map(|i| i.as_str()), Some("1"));
|
||||
assert_eq!(results[1]["size"], *"20");
|
||||
assert_eq!(results, vec![
|
||||
Segment::DiceRoll{ op: '+', count: 2, size: 6, filter: None },
|
||||
Segment::DiceRoll{ op: '+', count: 1, size: 20, filter: None },
|
||||
]);
|
||||
|
||||
results = parse_dice_segments("4d6d1");
|
||||
results = parse_dice_segments("4d6d1").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results.len(), 1);
|
||||
assert_eq!(results[0].name("drop").map(|i| i.as_str()), Some("1"));
|
||||
assert_eq!(results, vec![
|
||||
Segment::DiceRoll{ op: '+', count: 4, size: 6, filter: Some(DiceFilter::DropLowest(1)) },
|
||||
]);
|
||||
|
||||
results = parse_dice_segments("3d8+8");
|
||||
results = parse_dice_segments("3d8+8").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results.len(), 2);
|
||||
assert_eq!(results[1].name("op").map(|i| i.as_str()), Some("+"));
|
||||
assert_eq!(results[1].name("mod").map(|i| i.as_str()), Some("8"));
|
||||
assert_eq!(results, vec![
|
||||
Segment::DiceRoll{ op: '+', count: 3, size: 8, filter: None },
|
||||
Segment::Modifier{ op: '+', amount: 8 },
|
||||
]);
|
||||
|
||||
results = parse_dice_segments("3d8kl3 - 2d1dh1 / 2").expect("Failed to unpack results");
|
||||
|
||||
assert_eq!(results, vec![
|
||||
Segment::DiceRoll{ op: '+', count: 3, size: 8, filter: Some(DiceFilter::KeepLowest(3)) },
|
||||
Segment::DiceRoll{ op: '-', count: 2, size: 1, filter: Some(DiceFilter::DropHighest(1)) },
|
||||
Segment::Modifier{ op: '/', amount: 2 },
|
||||
]);
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user