forked from projectmoon/tenebrous-dicebot
Do not panic on invalid dice/sides amount for keep/drop.
Insted of unwrap(), map error to a nom parser error. Not the best-est solution, but it is functional. The TooLarge value seems appropriate.
This commit is contained in:
parent
7e7e9e534e
commit
126548d868
|
@ -4,6 +4,8 @@
|
||||||
* project.
|
* project.
|
||||||
*/
|
*/
|
||||||
use nom::bytes::complete::take_while;
|
use nom::bytes::complete::take_while;
|
||||||
|
use nom::error::ErrorKind as NomErrorKind;
|
||||||
|
use nom::Err as NomErr;
|
||||||
use nom::{
|
use nom::{
|
||||||
alt, bytes::complete::tag, character::complete::digit1, complete, many0, named,
|
alt, bytes::complete::tag, character::complete::digit1, complete, many0, named,
|
||||||
sequence::tuple, tag, IResult,
|
sequence::tuple, tag, IResult,
|
||||||
|
@ -31,6 +33,12 @@ enum Sign {
|
||||||
Minus,
|
Minus,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
macro_rules! too_big {
|
||||||
|
($input: expr) => {
|
||||||
|
NomErr::Error(($input, NomErrorKind::TooLarge))
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
/// Parse a dice expression. Does not eat whitespace
|
/// Parse a dice expression. Does not eat whitespace
|
||||||
fn parse_dice(input: &str) -> IResult<&str, Dice> {
|
fn parse_dice(input: &str) -> IResult<&str, Dice> {
|
||||||
// parse main dice expression
|
// parse main dice expression
|
||||||
|
@ -41,7 +49,7 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> {
|
||||||
// if ok, keep expression is present
|
// if ok, keep expression is present
|
||||||
Ok((rest, (_, keep_amount))) => (keep_amount, rest),
|
Ok((rest, (_, keep_amount))) => (keep_amount, rest),
|
||||||
// otherwise absent and keep all dice
|
// otherwise absent and keep all dice
|
||||||
Err(_) => ("", input)
|
Err(_) => ("", input),
|
||||||
};
|
};
|
||||||
|
|
||||||
// check for drop expression to drop highest dice (2d20dh1)
|
// check for drop expression to drop highest dice (2d20dh1)
|
||||||
|
@ -49,10 +57,10 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> {
|
||||||
// if ok, keep expression is present
|
// if ok, keep expression is present
|
||||||
Ok((rest, (_, drop_amount))) => (drop_amount, rest),
|
Ok((rest, (_, drop_amount))) => (drop_amount, rest),
|
||||||
// otherwise absent and keep all dice
|
// otherwise absent and keep all dice
|
||||||
Err(_) => ("", input)
|
Err(_) => ("", input),
|
||||||
};
|
};
|
||||||
|
|
||||||
let count: u32 = count.parse().unwrap();
|
let count: u32 = count.parse().map_err(|_| too_big!(count))?;
|
||||||
|
|
||||||
// don't allow keep greater than number of dice, and don't allow keep zero
|
// don't allow keep greater than number of dice, and don't allow keep zero
|
||||||
let keep_drop = match keep.parse::<u32>() {
|
let keep_drop = match keep.parse::<u32>() {
|
||||||
|
@ -72,13 +80,11 @@ fn parse_dice(input: &str) -> IResult<&str, Dice> {
|
||||||
// Err, there's neither keep nor drop
|
// Err, there's neither keep nor drop
|
||||||
Err(_) => KeepOrDrop::None,
|
Err(_) => KeepOrDrop::None,
|
||||||
}
|
}
|
||||||
},
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
Ok((
|
let sides = sides.parse().map_err(|_| too_big!(sides))?;
|
||||||
input,
|
Ok((input, Dice::new(count, sides, keep_drop)))
|
||||||
Dice::new(count, sides.parse().unwrap(), keep_drop),
|
|
||||||
))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse a single digit expression. Does not eat whitespace
|
// Parse a single digit expression. Does not eat whitespace
|
||||||
|
@ -149,18 +155,80 @@ mod tests {
|
||||||
use super::*;
|
use super::*;
|
||||||
#[test]
|
#[test]
|
||||||
fn dice_test() {
|
fn dice_test() {
|
||||||
assert_eq!(parse_dice("2d4"), Ok(("", Dice::new(2, 4, KeepOrDrop::None))));
|
assert_eq!(
|
||||||
assert_eq!(parse_dice("20d40"), Ok(("", Dice::new(20, 40, KeepOrDrop::None))));
|
parse_dice("2d4"),
|
||||||
assert_eq!(parse_dice("8d7"), Ok(("", Dice::new(8, 7, KeepOrDrop::None))));
|
Ok(("", Dice::new(2, 4, KeepOrDrop::None)))
|
||||||
assert_eq!(parse_dice("2d20k1"), Ok(("", Dice::new(2, 20, KeepOrDrop::Keep(1)))));
|
);
|
||||||
assert_eq!(parse_dice("100d10k90"), Ok(("", Dice::new(100, 10, KeepOrDrop::Keep(90)))));
|
assert_eq!(
|
||||||
assert_eq!(parse_dice("11d10k10"), Ok(("", Dice::new(11, 10, KeepOrDrop::Keep(10)))));
|
parse_dice("20d40"),
|
||||||
assert_eq!(parse_dice("12d10k11"), Ok(("", Dice::new(12, 10, KeepOrDrop::Keep(11)))));
|
Ok(("", Dice::new(20, 40, KeepOrDrop::None)))
|
||||||
assert_eq!(parse_dice("12d10k13"), Ok(("", Dice::new(12, 10, KeepOrDrop::None))));
|
);
|
||||||
assert_eq!(parse_dice("12d10k0"), Ok(("", Dice::new(12, 10, KeepOrDrop::None))));
|
assert_eq!(
|
||||||
assert_eq!(parse_dice("20d40dh5"), Ok(("", Dice::new(20, 40, KeepOrDrop::Drop(5)))));
|
parse_dice("8d7"),
|
||||||
assert_eq!(parse_dice("8d7dh9"), Ok(("", Dice::new(8, 7, KeepOrDrop::None))));
|
Ok(("", Dice::new(8, 7, KeepOrDrop::None)))
|
||||||
assert_eq!(parse_dice("8d7dh8"), Ok(("", Dice::new(8, 7, KeepOrDrop::None))));
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("2d20k1"),
|
||||||
|
Ok(("", Dice::new(2, 20, KeepOrDrop::Keep(1))))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("100d10k90"),
|
||||||
|
Ok(("", Dice::new(100, 10, KeepOrDrop::Keep(90))))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("11d10k10"),
|
||||||
|
Ok(("", Dice::new(11, 10, KeepOrDrop::Keep(10))))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("12d10k11"),
|
||||||
|
Ok(("", Dice::new(12, 10, KeepOrDrop::Keep(11))))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("12d10k13"),
|
||||||
|
Ok(("", Dice::new(12, 10, KeepOrDrop::None)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("12d10k0"),
|
||||||
|
Ok(("", Dice::new(12, 10, KeepOrDrop::None)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("20d40dh5"),
|
||||||
|
Ok(("", Dice::new(20, 40, KeepOrDrop::Drop(5))))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("8d7dh9"),
|
||||||
|
Ok(("", Dice::new(8, 7, KeepOrDrop::None)))
|
||||||
|
);
|
||||||
|
assert_eq!(
|
||||||
|
parse_dice("8d7dh8"),
|
||||||
|
Ok(("", Dice::new(8, 7, KeepOrDrop::None)))
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn big_number_of_dice_doesnt_crash_test() {
|
||||||
|
let res = parse_dice("64378631476346123874527551481376547657868536d4");
|
||||||
|
assert!(res.is_err());
|
||||||
|
match res {
|
||||||
|
Err(NomErr::Error((input, kind))) => {
|
||||||
|
assert_eq!(kind, NomErrorKind::TooLarge);
|
||||||
|
assert_eq!(input, "64378631476346123874527551481376547657868536");
|
||||||
|
}
|
||||||
|
_ => panic!("Got success, expected error"),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn big_number_of_sides_doesnt_crash_test() {
|
||||||
|
let res = parse_dice("1d423562312587425472658956278456298376234876");
|
||||||
|
assert!(res.is_err());
|
||||||
|
match res {
|
||||||
|
Err(NomErr::Error((input, kind))) => {
|
||||||
|
assert_eq!(kind, NomErrorKind::TooLarge);
|
||||||
|
assert_eq!(input, "423562312587425472658956278456298376234876");
|
||||||
|
}
|
||||||
|
_ => panic!("Got success, expected error"),
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
|
@ -219,7 +287,9 @@ mod tests {
|
||||||
Ok((
|
Ok((
|
||||||
"",
|
"",
|
||||||
ElementExpression(vec![SignedElement::Positive(Element::Dice(Dice::new(
|
ElementExpression(vec![SignedElement::Positive(Element::Dice(Dice::new(
|
||||||
8, 4, KeepOrDrop::None
|
8,
|
||||||
|
4,
|
||||||
|
KeepOrDrop::None
|
||||||
)))])
|
)))])
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
@ -238,7 +308,9 @@ mod tests {
|
||||||
Ok((
|
Ok((
|
||||||
" \n ",
|
" \n ",
|
||||||
ElementExpression(vec![SignedElement::Negative(Element::Dice(Dice::new(
|
ElementExpression(vec![SignedElement::Negative(Element::Dice(Dice::new(
|
||||||
8, 4, KeepOrDrop::None
|
8,
|
||||||
|
4,
|
||||||
|
KeepOrDrop::None
|
||||||
)))])
|
)))])
|
||||||
))
|
))
|
||||||
);
|
);
|
||||||
|
|
Loading…
Reference in New Issue