pub enum DiceExpr {
Sum(Box<DiceExpr>, Box<DiceExpr>),
Difference(Box<DiceExpr>, Box<DiceExpr>),
Roll(RollSpec),
Literal(i32),
}Expand description
A parsed dice expression that can be evaluated to produce a result.
This is the main type for working with dice expressions. It represents a tree structure of dice rolls, literals, and arithmetic operations.
§Variants
Sum: Addition of two sub-expressions (e.g., “2d6 + 3”)Difference: Subtraction of two sub-expressions (e.g., “1d20 - 2”)Roll: A dice roll specification (e.g., “2d6”)Literal: A constant integer value (e.g., “5”)
§Examples
§Parsing from a string
use dice_parser::DiceExpr;
let expr = DiceExpr::parse("2d6+3").unwrap();
let result = expr.roll().unwrap();
assert!(result.total >= 5 && result.total <= 15); // 2-12 from dice + 3§Manual construction
use dice_parser::{DiceExpr, RollSpec};
// Create "1d20 + 5"
let roll = DiceExpr::Roll(RollSpec::new(1, 20, None));
let modifier = DiceExpr::Literal(5);
let expr = DiceExpr::Sum(Box::new(roll), Box::new(modifier));
let result = expr.roll().unwrap();
assert!(result.total >= 6 && result.total <= 25);Variants§
Sum(Box<DiceExpr>, Box<DiceExpr>)
Addition of two dice expressions.
Difference(Box<DiceExpr>, Box<DiceExpr>)
Subtraction of two dice expressions.
Roll(RollSpec)
A dice roll specification.
Literal(i32)
A constant integer literal.
Implementations§
Source§impl DiceExpr
impl DiceExpr
Sourcepub fn roll(&self) -> Result<ExprResult, DiceError>
pub fn roll(&self) -> Result<ExprResult, DiceError>
Evaluate the dice expression using the default random number generator provided by the rand crate.
This method rolls all dice in the expression and computes the final result, including individual roll values and any modifiers.
§Returns
Ok(ExprResult): The result of evaluating the expressionErr(DiceError): If the roll specification is invalid or an overflow occurs
§Examples
use dice_parser::DiceExpr;
let expr = DiceExpr::parse("2d6+3").unwrap();
let result = expr.roll().unwrap();
// Result contains total, individual rolls, and modifier
assert!(result.total >= 5 && result.total <= 15);
assert_eq!(result.rolls.len(), 2); // Two d6 rolls
assert_eq!(result.modifier, 3);Examples found in repository?
5fn main() {
6 println!("=== Error Handling Examples ===\n");
7
8 // Parse error - invalid syntax
9 println!("1. Parse error (missing sides):");
10 match DiceExpr::parse("2d") {
11 Ok(_) => println!(" Unexpectedly succeeded!"),
12 Err(e) => println!(" Error: {}", e),
13 }
14 println!();
15
16 // Syntax error - negative dice count
17 println!("2. Syntax error (negative dice count):");
18 match DiceExpr::parse("-2d6") {
19 Ok(_) => println!(" Unexpectedly succeeded!"),
20 Err(e) => println!(" Error: {}", e),
21 }
22 println!();
23
24 // Trailing input error
25 println!("3. Trailing input error:");
26 match DiceExpr::parse("2d6 extra") {
27 Ok(_) => println!(" Unexpectedly succeeded!"),
28 Err(e) => println!(" Error: {}", e),
29 }
30 println!();
31
32 // Invalid spec error (keep more than rolled)
33 println!("4. Invalid roll specification:");
34 use dice_parser::{RollSpec, Keep};
35 let spec = RollSpec::new(2, 6, Some(Keep::Highest(5))); // Try to keep 5 dice when only rolling 2
36 let expr = DiceExpr::Roll(spec);
37 match expr.roll() {
38 Ok(_) => println!(" Unexpectedly succeeded!"),
39 Err(e) => println!(" Error: {}", e),
40 }
41 println!();
42
43 // Pattern matching on error types
44 println!("5. Pattern matching on DiceError:");
45 let result = DiceExpr::parse("abc");
46 match result {
47 Ok(_) => println!(" Unexpectedly succeeded!"),
48 Err(DiceError::ParseError { kind, input, start, stop: _ }) => {
49 println!(" Parse error at position {}: {:?}", start, kind);
50 println!(" Input: {}", input);
51 }
52 Err(e) => println!(" Other error: {}", e),
53 }
54}More examples
5fn main() {
6 println!("=== Basic Parsing and Rolling ===");
7 let expr = DiceExpr::parse("2d6+3").unwrap();
8 let result = expr.roll().unwrap();
9 println!("Expression: 2d6+3");
10 println!("Total: {}", result.total);
11 println!("Rolls: {:?}", result.rolls);
12 println!("Modifier: {}", result.modifier);
13 println!();
14
15 println!("=== Manual Construction ===");
16 let roll_spec = RollSpec::new(4, 6, Some(Keep::Highest(3)));
17 let expr = DiceExpr::Roll(roll_spec);
18 let result = expr.roll().unwrap();
19 println!("Expression: 4d6 keep highest 3");
20 println!("Total: {}", result.total);
21 println!("All rolls: {:?}", result.rolls);
22 println!();
23
24 println!("=== Complex Expression ===");
25 let d2d6 = DiceExpr::Roll(RollSpec::new(2, 6, None));
26 let d1d4 = DiceExpr::Roll(RollSpec::new(1, 4, None));
27 let modifier = DiceExpr::Literal(2);
28 let sum = DiceExpr::Sum(Box::new(d2d6), Box::new(d1d4));
29 let expr = DiceExpr::Difference(Box::new(sum), Box::new(modifier));
30 let result = expr.roll().unwrap();
31 println!("Expression: (2d6 + 1d4) - 2");
32 println!("Total: {}", result.total);
33 println!();
34
35 println!("=== D&D 5e Advantage ===");
36 let advantage = DiceExpr::Roll(RollSpec::new(2, 20, Some(Keep::Highest(1))));
37 let result = advantage.roll().unwrap();
38 println!("Expression: 2d20 keep highest 1 (advantage)");
39 println!("Rolls: {:?}", result.rolls);
40 println!("Result: {}", result.total);
41 println!();
42
43 println!("=== D&D 5e Disadvantage ===");
44 let disadvantage = DiceExpr::Roll(RollSpec::new(2, 20, Some(Keep::Lowest(1))));
45 let result = disadvantage.roll().unwrap();
46 println!("Expression: 2d20 keep lowest 1 (disadvantage)");
47 println!("Rolls: {:?}", result.rolls);
48 println!("Result: {}", result.total);
49 println!();
50
51 println!("=== Character Ability Scores ===");
52 println!("Rolling 4d6 drop lowest for 6 abilities:");
53 let ability_roll = DiceExpr::Roll(RollSpec::new(4, 6, Some(Keep::Highest(3))));
54 for i in 0..6 {
55 let result = ability_roll.roll().unwrap();
56 println!(" Ability {}: {} (rolls: {:?})", i + 1, result.total, result.rolls);
57 }
58}8fn main() {
9 // ========================================
10 // 1. DiceExpr - The main expression type
11 // ========================================
12
13 // 1.1 Parsing from string
14 println!("1. Parsing and expressions");
15 let parsed = DiceExpr::parse("2d6+3").unwrap();
16 println!(" 1.1 Parsing e.g. \"2d6+3\" yields:");
17 println!(" {:?}", parsed);
18
19 // 1.2 DiceExpr variants - all publicly accessible
20 let literal = DiceExpr::Literal(5);
21 println!(" 1.2 A literal expression \"5\": ");
22 println!(" {:?}", literal);
23
24 let roll = DiceExpr::Roll(RollSpec::new(2, 6, None));
25 println!(" 1.3 A single roll expression corresponding to 2d6:",);
26 println!(" {:?}", roll);
27
28 let sum = DiceExpr::Sum(Box::new(roll.clone()), Box::new(literal.clone()));
29 println!(" 1.4 A sum of two expression, e.g. \"2d6+5\":");
30 println!(" {:?}", sum);
31
32 let _diff = DiceExpr::Difference(Box::new(roll.clone()), Box::new(DiceExpr::Literal(1)));
33 println!(" 1.5 A difference of two expressions, e.g. \"2d6-1\":",);
34 println!(" {:?}", _diff);
35
36 // 1.3 Rolling methods
37 println!("\n 1.6 DiceExpr::roll() - Rolling expressions");
38 let result1 = parsed.roll().unwrap();
39 println!(" An expression, 2d6+3: {}", result1.total);
40 let result2 = literal.roll().unwrap();
41 println!(" A literal 5: {}", result2.total);
42 let result3 = roll.roll().unwrap();
43 println!(" A single roll 2d6: {}", result3.total);
44 let result4 = sum.roll().unwrap();
45 println!(" A manual sum, 2d6+5: {}", result4.total);
46 let result5 = _diff.roll().unwrap();
47 println!(" A manual diff, 2d6-1: {}", result5.total);
48
49 println!("\n 1.7 DiceExpr::roll_with_rng() - Roll with custom RNG");
50 let rng = StdRng::seed_from_u64(42);
51 let result6 = DiceExpr::parse("1d20").unwrap().roll_with_rng(rng).unwrap();
52 println!(" Rolling 1d20: {}", result6.total);
53
54 // ========================================
55 // 2. RollSpec - Dice roll specification
56 // ========================================
57 println!("\n2. RollSpec Usage:");
58 println!(" Struct with public fields and constructor\n");
59
60 let spec = RollSpec::new(4, 6, Some(Keep::Highest(3)));
61 println!(" RollSpec::new(4, 6, Some(Keep::Highest(3)))");
62 println!(" - count: {}", spec.count);
63 println!(" - sides: {}", spec.sides);
64 println!(" - keep: {:?}", spec.keep);
65
66 // ========================================
67 // 3. Keep - Keep modifier enum
68 // ========================================
69 println!("\n3. Keep Usage:");
70 println!(" Enum with public variants\n");
71
72 let _keep_high = Keep::Highest(3);
73 println!(" {:?} - Keep 3 highest dice", _keep_high);
74
75 let _keep_low = Keep::Lowest(1);
76 println!(" {:?} - Keep 1 lowest die", _keep_high);
77
78 // Use in RollSpec
79 let _advantage = RollSpec::new(2, 20, Some(Keep::Highest(1)));
80 let _disadvantage = RollSpec::new(2, 20, Some(Keep::Lowest(1)));
81 println!("\n Example with Advantage:");
82 println!(
83 " {:?}, result: {}",
84 _advantage,
85 DiceExpr::Roll(_advantage.clone()).roll().unwrap().total
86 );
87 println!("\n Example with Disadvantage:");
88 println!(
89 " {:?}, result: {}",
90 _disadvantage,
91 DiceExpr::Roll(_disadvantage.clone()).roll().unwrap().total
92 );
93
94 // ========================================
95 // 4. ExprResult - Roll result
96 // ========================================
97 println!("\n4. ExprResult Usage:");
98 println!(" Struct with public fields\n");
99
100 let expr = DiceExpr::parse("2d6+3").unwrap();
101 let result: ExprResult = expr.roll().unwrap();
102
103 println!(" ExprResult from rolling 2d6+3:");
104 println!(" - total: {} (final result)", result.total);
105 println!(" - rolls: {:?} (individual dice)", result.rolls);
106 println!(" - modifier: {} (constant modifiers)", result.modifier);
107
108 // ========================================
109 // 5. DiceError - Error handling
110 // ========================================
111 println!("\n5. DiceError Usage:");
112 println!(" Enum with public variants for error handling\n");
113
114 // ParseError variant
115 if let Err(DiceError::ParseError {
116 kind,
117 input,
118 start,
119 stop: _,
120 }) = DiceExpr::parse("invalid")
121 {
122 println!(" DiceError::ParseError:");
123 println!(" - kind: {:?}", kind);
124 println!(" - input: {}", input);
125 println!(" - start: {}", start);
126 }
127
128 // SyntaxError variant
129 if let Err(DiceError::SyntaxError { expected, .. }) = DiceExpr::parse("-2d6") {
130 println!("\n DiceError::SyntaxError:");
131 if let Some(exp) = expected {
132 println!(" - expected: {}", exp);
133 }
134 }
135
136 // TrailingInput variant
137 if let Err(DiceError::TrailingInput { pos, .. }) = DiceExpr::parse("2d6 extra") {
138 println!("\n DiceError::TrailingInput:");
139 println!(" - pos: {}", pos);
140 }
141
142 // InvalidSpec variant
143 let bad_spec = RollSpec::new(2, 6, Some(Keep::Highest(5)));
144 if let Err(DiceError::InvalidSpec(spec, msg)) = DiceExpr::Roll(bad_spec).roll() {
145 println!("\n DiceError::InvalidSpec:");
146 println!(" - spec: {:?}", spec);
147 println!(" - message: {}", msg);
148 }
149
150 // ========================================
151 // 6. Complete Example
152 // ========================================
153 println!("\n6. Complete Example:");
154 println!(" Building a complex expression with all features\n");
155
156 // Character creation: 6 abilities, each 4d6 keep highest 3
157 println!(" Rolling D&D ability scores (4d6 drop lowest):");
158 let ability_roll = DiceExpr::Roll(RollSpec::new(4, 6, Some(Keep::Highest(3))));
159
160 for i in 1..=6 {
161 if let Ok(result) = ability_roll.roll() {
162 println!(
163 " Ability {}: {} (rolls: {:?})",
164 i, result.total, result.rolls
165 );
166 }
167 }
168}Sourcepub fn roll_with_rng<T: Rng>(&self, r: T) -> Result<ExprResult, DiceError>
pub fn roll_with_rng<T: Rng>(&self, r: T) -> Result<ExprResult, DiceError>
Evaluate the dice expression using a custom random number generator.
This method is useful for deterministic testing or when you want to control the randomness source.
§Parameters
r: Any type implementing therand::Rngtrait
§Returns
Ok(ExprResult): The result of evaluating the expressionErr(DiceError): If the roll specification is invalid or an overflow occurs
§Examples
use dice_parser::DiceExpr;
use rand::{SeedableRng, rngs::StdRng};
let expr = DiceExpr::parse("1d20").unwrap();
// Use a seeded RNG for deterministic results
let rng = StdRng::seed_from_u64(42);
let result = expr.roll_with_rng(rng).unwrap();
assert!(result.total >= 1 && result.total <= 20);Examples found in repository?
6fn main() {
7 println!("=== Deterministic Rolling with Custom RNG ===\n");
8
9 let expr = DiceExpr::parse("2d6+3").unwrap();
10
11 println!("Rolling the same expression with the same seed produces identical results:\n");
12
13 // First roll with seed 42
14 let rng1 = StdRng::seed_from_u64(42);
15 let result1 = expr.roll_with_rng(rng1).unwrap();
16 println!("First roll (seed 42):");
17 println!(" Total: {}", result1.total);
18 println!(" Rolls: {:?}", result1.rolls);
19
20 // Second roll with the same seed should give the same result
21 let rng2 = StdRng::seed_from_u64(42);
22 let result2 = expr.roll_with_rng(rng2).unwrap();
23 println!("\nSecond roll (seed 42):");
24 println!(" Total: {}", result2.total);
25 println!(" Rolls: {:?}", result2.rolls);
26
27 assert_eq!(result1.total, result2.total);
28 assert_eq!(result1.rolls, result2.rolls);
29 println!("\n✓ Results match as expected!");
30
31 // Different seed produces different results
32 let rng3 = StdRng::seed_from_u64(123);
33 let result3 = expr.roll_with_rng(rng3).unwrap();
34 println!("\nThird roll (seed 123):");
35 println!(" Total: {}", result3.total);
36 println!(" Rolls: {:?}", result3.rolls);
37 println!("\n✓ Different seed produces different results (usually)");
38}More examples
8fn main() {
9 // ========================================
10 // 1. DiceExpr - The main expression type
11 // ========================================
12
13 // 1.1 Parsing from string
14 println!("1. Parsing and expressions");
15 let parsed = DiceExpr::parse("2d6+3").unwrap();
16 println!(" 1.1 Parsing e.g. \"2d6+3\" yields:");
17 println!(" {:?}", parsed);
18
19 // 1.2 DiceExpr variants - all publicly accessible
20 let literal = DiceExpr::Literal(5);
21 println!(" 1.2 A literal expression \"5\": ");
22 println!(" {:?}", literal);
23
24 let roll = DiceExpr::Roll(RollSpec::new(2, 6, None));
25 println!(" 1.3 A single roll expression corresponding to 2d6:",);
26 println!(" {:?}", roll);
27
28 let sum = DiceExpr::Sum(Box::new(roll.clone()), Box::new(literal.clone()));
29 println!(" 1.4 A sum of two expression, e.g. \"2d6+5\":");
30 println!(" {:?}", sum);
31
32 let _diff = DiceExpr::Difference(Box::new(roll.clone()), Box::new(DiceExpr::Literal(1)));
33 println!(" 1.5 A difference of two expressions, e.g. \"2d6-1\":",);
34 println!(" {:?}", _diff);
35
36 // 1.3 Rolling methods
37 println!("\n 1.6 DiceExpr::roll() - Rolling expressions");
38 let result1 = parsed.roll().unwrap();
39 println!(" An expression, 2d6+3: {}", result1.total);
40 let result2 = literal.roll().unwrap();
41 println!(" A literal 5: {}", result2.total);
42 let result3 = roll.roll().unwrap();
43 println!(" A single roll 2d6: {}", result3.total);
44 let result4 = sum.roll().unwrap();
45 println!(" A manual sum, 2d6+5: {}", result4.total);
46 let result5 = _diff.roll().unwrap();
47 println!(" A manual diff, 2d6-1: {}", result5.total);
48
49 println!("\n 1.7 DiceExpr::roll_with_rng() - Roll with custom RNG");
50 let rng = StdRng::seed_from_u64(42);
51 let result6 = DiceExpr::parse("1d20").unwrap().roll_with_rng(rng).unwrap();
52 println!(" Rolling 1d20: {}", result6.total);
53
54 // ========================================
55 // 2. RollSpec - Dice roll specification
56 // ========================================
57 println!("\n2. RollSpec Usage:");
58 println!(" Struct with public fields and constructor\n");
59
60 let spec = RollSpec::new(4, 6, Some(Keep::Highest(3)));
61 println!(" RollSpec::new(4, 6, Some(Keep::Highest(3)))");
62 println!(" - count: {}", spec.count);
63 println!(" - sides: {}", spec.sides);
64 println!(" - keep: {:?}", spec.keep);
65
66 // ========================================
67 // 3. Keep - Keep modifier enum
68 // ========================================
69 println!("\n3. Keep Usage:");
70 println!(" Enum with public variants\n");
71
72 let _keep_high = Keep::Highest(3);
73 println!(" {:?} - Keep 3 highest dice", _keep_high);
74
75 let _keep_low = Keep::Lowest(1);
76 println!(" {:?} - Keep 1 lowest die", _keep_high);
77
78 // Use in RollSpec
79 let _advantage = RollSpec::new(2, 20, Some(Keep::Highest(1)));
80 let _disadvantage = RollSpec::new(2, 20, Some(Keep::Lowest(1)));
81 println!("\n Example with Advantage:");
82 println!(
83 " {:?}, result: {}",
84 _advantage,
85 DiceExpr::Roll(_advantage.clone()).roll().unwrap().total
86 );
87 println!("\n Example with Disadvantage:");
88 println!(
89 " {:?}, result: {}",
90 _disadvantage,
91 DiceExpr::Roll(_disadvantage.clone()).roll().unwrap().total
92 );
93
94 // ========================================
95 // 4. ExprResult - Roll result
96 // ========================================
97 println!("\n4. ExprResult Usage:");
98 println!(" Struct with public fields\n");
99
100 let expr = DiceExpr::parse("2d6+3").unwrap();
101 let result: ExprResult = expr.roll().unwrap();
102
103 println!(" ExprResult from rolling 2d6+3:");
104 println!(" - total: {} (final result)", result.total);
105 println!(" - rolls: {:?} (individual dice)", result.rolls);
106 println!(" - modifier: {} (constant modifiers)", result.modifier);
107
108 // ========================================
109 // 5. DiceError - Error handling
110 // ========================================
111 println!("\n5. DiceError Usage:");
112 println!(" Enum with public variants for error handling\n");
113
114 // ParseError variant
115 if let Err(DiceError::ParseError {
116 kind,
117 input,
118 start,
119 stop: _,
120 }) = DiceExpr::parse("invalid")
121 {
122 println!(" DiceError::ParseError:");
123 println!(" - kind: {:?}", kind);
124 println!(" - input: {}", input);
125 println!(" - start: {}", start);
126 }
127
128 // SyntaxError variant
129 if let Err(DiceError::SyntaxError { expected, .. }) = DiceExpr::parse("-2d6") {
130 println!("\n DiceError::SyntaxError:");
131 if let Some(exp) = expected {
132 println!(" - expected: {}", exp);
133 }
134 }
135
136 // TrailingInput variant
137 if let Err(DiceError::TrailingInput { pos, .. }) = DiceExpr::parse("2d6 extra") {
138 println!("\n DiceError::TrailingInput:");
139 println!(" - pos: {}", pos);
140 }
141
142 // InvalidSpec variant
143 let bad_spec = RollSpec::new(2, 6, Some(Keep::Highest(5)));
144 if let Err(DiceError::InvalidSpec(spec, msg)) = DiceExpr::Roll(bad_spec).roll() {
145 println!("\n DiceError::InvalidSpec:");
146 println!(" - spec: {:?}", spec);
147 println!(" - message: {}", msg);
148 }
149
150 // ========================================
151 // 6. Complete Example
152 // ========================================
153 println!("\n6. Complete Example:");
154 println!(" Building a complex expression with all features\n");
155
156 // Character creation: 6 abilities, each 4d6 keep highest 3
157 println!(" Rolling D&D ability scores (4d6 drop lowest):");
158 let ability_roll = DiceExpr::Roll(RollSpec::new(4, 6, Some(Keep::Highest(3))));
159
160 for i in 1..=6 {
161 if let Ok(result) = ability_roll.roll() {
162 println!(
163 " Ability {}: {} (rolls: {:?})",
164 i, result.total, result.rolls
165 );
166 }
167 }
168}Sourcepub fn parse(input: &str) -> Result<DiceExpr, DiceError>
pub fn parse(input: &str) -> Result<DiceExpr, DiceError>
Parse a dice expression from a string.
This is the prefred way to create a DiceExpr. This is especially useful when parsin user input.
The parser supports a subset standard dice notation with addition and subtraction.
§Supported Syntax
- Dice rolls:
NdSwhere N is the number of dice and S is the number of sides - Literals: Any integer (positive or negative)
- Addition:
expr + expr - Subtraction:
expr - expr - Whitespace is ignored
Note: Keep mechanics (e.g., “2d20kh” for keep highest, “6d6kl3” for keep lowest 3)
are planned for a future release. Currently, use manual construction with
RollSpec and Keep for keep functionality.
§Parameters
input: A string slice containing the dice expression
§Returns
Ok(DiceExpr): The parsed expressionErr(DiceError): If the input is malformed or contains syntax errors
§Examples
use dice_parser::DiceExpr;
// Simple dice roll
let expr = DiceExpr::parse("2d6").unwrap();
// With addition
let expr = DiceExpr::parse("1d20 + 5").unwrap();
// Complex expression
let expr = DiceExpr::parse("2d6 + 1d4 - 2").unwrap();
// Negative dice count is not allowed
assert!(DiceExpr::parse("-2d6").is_err());Examples found in repository?
6fn main() {
7 println!("=== Deterministic Rolling with Custom RNG ===\n");
8
9 let expr = DiceExpr::parse("2d6+3").unwrap();
10
11 println!("Rolling the same expression with the same seed produces identical results:\n");
12
13 // First roll with seed 42
14 let rng1 = StdRng::seed_from_u64(42);
15 let result1 = expr.roll_with_rng(rng1).unwrap();
16 println!("First roll (seed 42):");
17 println!(" Total: {}", result1.total);
18 println!(" Rolls: {:?}", result1.rolls);
19
20 // Second roll with the same seed should give the same result
21 let rng2 = StdRng::seed_from_u64(42);
22 let result2 = expr.roll_with_rng(rng2).unwrap();
23 println!("\nSecond roll (seed 42):");
24 println!(" Total: {}", result2.total);
25 println!(" Rolls: {:?}", result2.rolls);
26
27 assert_eq!(result1.total, result2.total);
28 assert_eq!(result1.rolls, result2.rolls);
29 println!("\n✓ Results match as expected!");
30
31 // Different seed produces different results
32 let rng3 = StdRng::seed_from_u64(123);
33 let result3 = expr.roll_with_rng(rng3).unwrap();
34 println!("\nThird roll (seed 123):");
35 println!(" Total: {}", result3.total);
36 println!(" Rolls: {:?}", result3.rolls);
37 println!("\n✓ Different seed produces different results (usually)");
38}More examples
5fn main() {
6 println!("=== Error Handling Examples ===\n");
7
8 // Parse error - invalid syntax
9 println!("1. Parse error (missing sides):");
10 match DiceExpr::parse("2d") {
11 Ok(_) => println!(" Unexpectedly succeeded!"),
12 Err(e) => println!(" Error: {}", e),
13 }
14 println!();
15
16 // Syntax error - negative dice count
17 println!("2. Syntax error (negative dice count):");
18 match DiceExpr::parse("-2d6") {
19 Ok(_) => println!(" Unexpectedly succeeded!"),
20 Err(e) => println!(" Error: {}", e),
21 }
22 println!();
23
24 // Trailing input error
25 println!("3. Trailing input error:");
26 match DiceExpr::parse("2d6 extra") {
27 Ok(_) => println!(" Unexpectedly succeeded!"),
28 Err(e) => println!(" Error: {}", e),
29 }
30 println!();
31
32 // Invalid spec error (keep more than rolled)
33 println!("4. Invalid roll specification:");
34 use dice_parser::{RollSpec, Keep};
35 let spec = RollSpec::new(2, 6, Some(Keep::Highest(5))); // Try to keep 5 dice when only rolling 2
36 let expr = DiceExpr::Roll(spec);
37 match expr.roll() {
38 Ok(_) => println!(" Unexpectedly succeeded!"),
39 Err(e) => println!(" Error: {}", e),
40 }
41 println!();
42
43 // Pattern matching on error types
44 println!("5. Pattern matching on DiceError:");
45 let result = DiceExpr::parse("abc");
46 match result {
47 Ok(_) => println!(" Unexpectedly succeeded!"),
48 Err(DiceError::ParseError { kind, input, start, stop: _ }) => {
49 println!(" Parse error at position {}: {:?}", start, kind);
50 println!(" Input: {}", input);
51 }
52 Err(e) => println!(" Other error: {}", e),
53 }
54}5fn main() {
6 println!("=== Basic Parsing and Rolling ===");
7 let expr = DiceExpr::parse("2d6+3").unwrap();
8 let result = expr.roll().unwrap();
9 println!("Expression: 2d6+3");
10 println!("Total: {}", result.total);
11 println!("Rolls: {:?}", result.rolls);
12 println!("Modifier: {}", result.modifier);
13 println!();
14
15 println!("=== Manual Construction ===");
16 let roll_spec = RollSpec::new(4, 6, Some(Keep::Highest(3)));
17 let expr = DiceExpr::Roll(roll_spec);
18 let result = expr.roll().unwrap();
19 println!("Expression: 4d6 keep highest 3");
20 println!("Total: {}", result.total);
21 println!("All rolls: {:?}", result.rolls);
22 println!();
23
24 println!("=== Complex Expression ===");
25 let d2d6 = DiceExpr::Roll(RollSpec::new(2, 6, None));
26 let d1d4 = DiceExpr::Roll(RollSpec::new(1, 4, None));
27 let modifier = DiceExpr::Literal(2);
28 let sum = DiceExpr::Sum(Box::new(d2d6), Box::new(d1d4));
29 let expr = DiceExpr::Difference(Box::new(sum), Box::new(modifier));
30 let result = expr.roll().unwrap();
31 println!("Expression: (2d6 + 1d4) - 2");
32 println!("Total: {}", result.total);
33 println!();
34
35 println!("=== D&D 5e Advantage ===");
36 let advantage = DiceExpr::Roll(RollSpec::new(2, 20, Some(Keep::Highest(1))));
37 let result = advantage.roll().unwrap();
38 println!("Expression: 2d20 keep highest 1 (advantage)");
39 println!("Rolls: {:?}", result.rolls);
40 println!("Result: {}", result.total);
41 println!();
42
43 println!("=== D&D 5e Disadvantage ===");
44 let disadvantage = DiceExpr::Roll(RollSpec::new(2, 20, Some(Keep::Lowest(1))));
45 let result = disadvantage.roll().unwrap();
46 println!("Expression: 2d20 keep lowest 1 (disadvantage)");
47 println!("Rolls: {:?}", result.rolls);
48 println!("Result: {}", result.total);
49 println!();
50
51 println!("=== Character Ability Scores ===");
52 println!("Rolling 4d6 drop lowest for 6 abilities:");
53 let ability_roll = DiceExpr::Roll(RollSpec::new(4, 6, Some(Keep::Highest(3))));
54 for i in 0..6 {
55 let result = ability_roll.roll().unwrap();
56 println!(" Ability {}: {} (rolls: {:?})", i + 1, result.total, result.rolls);
57 }
58}8fn main() {
9 // ========================================
10 // 1. DiceExpr - The main expression type
11 // ========================================
12
13 // 1.1 Parsing from string
14 println!("1. Parsing and expressions");
15 let parsed = DiceExpr::parse("2d6+3").unwrap();
16 println!(" 1.1 Parsing e.g. \"2d6+3\" yields:");
17 println!(" {:?}", parsed);
18
19 // 1.2 DiceExpr variants - all publicly accessible
20 let literal = DiceExpr::Literal(5);
21 println!(" 1.2 A literal expression \"5\": ");
22 println!(" {:?}", literal);
23
24 let roll = DiceExpr::Roll(RollSpec::new(2, 6, None));
25 println!(" 1.3 A single roll expression corresponding to 2d6:",);
26 println!(" {:?}", roll);
27
28 let sum = DiceExpr::Sum(Box::new(roll.clone()), Box::new(literal.clone()));
29 println!(" 1.4 A sum of two expression, e.g. \"2d6+5\":");
30 println!(" {:?}", sum);
31
32 let _diff = DiceExpr::Difference(Box::new(roll.clone()), Box::new(DiceExpr::Literal(1)));
33 println!(" 1.5 A difference of two expressions, e.g. \"2d6-1\":",);
34 println!(" {:?}", _diff);
35
36 // 1.3 Rolling methods
37 println!("\n 1.6 DiceExpr::roll() - Rolling expressions");
38 let result1 = parsed.roll().unwrap();
39 println!(" An expression, 2d6+3: {}", result1.total);
40 let result2 = literal.roll().unwrap();
41 println!(" A literal 5: {}", result2.total);
42 let result3 = roll.roll().unwrap();
43 println!(" A single roll 2d6: {}", result3.total);
44 let result4 = sum.roll().unwrap();
45 println!(" A manual sum, 2d6+5: {}", result4.total);
46 let result5 = _diff.roll().unwrap();
47 println!(" A manual diff, 2d6-1: {}", result5.total);
48
49 println!("\n 1.7 DiceExpr::roll_with_rng() - Roll with custom RNG");
50 let rng = StdRng::seed_from_u64(42);
51 let result6 = DiceExpr::parse("1d20").unwrap().roll_with_rng(rng).unwrap();
52 println!(" Rolling 1d20: {}", result6.total);
53
54 // ========================================
55 // 2. RollSpec - Dice roll specification
56 // ========================================
57 println!("\n2. RollSpec Usage:");
58 println!(" Struct with public fields and constructor\n");
59
60 let spec = RollSpec::new(4, 6, Some(Keep::Highest(3)));
61 println!(" RollSpec::new(4, 6, Some(Keep::Highest(3)))");
62 println!(" - count: {}", spec.count);
63 println!(" - sides: {}", spec.sides);
64 println!(" - keep: {:?}", spec.keep);
65
66 // ========================================
67 // 3. Keep - Keep modifier enum
68 // ========================================
69 println!("\n3. Keep Usage:");
70 println!(" Enum with public variants\n");
71
72 let _keep_high = Keep::Highest(3);
73 println!(" {:?} - Keep 3 highest dice", _keep_high);
74
75 let _keep_low = Keep::Lowest(1);
76 println!(" {:?} - Keep 1 lowest die", _keep_high);
77
78 // Use in RollSpec
79 let _advantage = RollSpec::new(2, 20, Some(Keep::Highest(1)));
80 let _disadvantage = RollSpec::new(2, 20, Some(Keep::Lowest(1)));
81 println!("\n Example with Advantage:");
82 println!(
83 " {:?}, result: {}",
84 _advantage,
85 DiceExpr::Roll(_advantage.clone()).roll().unwrap().total
86 );
87 println!("\n Example with Disadvantage:");
88 println!(
89 " {:?}, result: {}",
90 _disadvantage,
91 DiceExpr::Roll(_disadvantage.clone()).roll().unwrap().total
92 );
93
94 // ========================================
95 // 4. ExprResult - Roll result
96 // ========================================
97 println!("\n4. ExprResult Usage:");
98 println!(" Struct with public fields\n");
99
100 let expr = DiceExpr::parse("2d6+3").unwrap();
101 let result: ExprResult = expr.roll().unwrap();
102
103 println!(" ExprResult from rolling 2d6+3:");
104 println!(" - total: {} (final result)", result.total);
105 println!(" - rolls: {:?} (individual dice)", result.rolls);
106 println!(" - modifier: {} (constant modifiers)", result.modifier);
107
108 // ========================================
109 // 5. DiceError - Error handling
110 // ========================================
111 println!("\n5. DiceError Usage:");
112 println!(" Enum with public variants for error handling\n");
113
114 // ParseError variant
115 if let Err(DiceError::ParseError {
116 kind,
117 input,
118 start,
119 stop: _,
120 }) = DiceExpr::parse("invalid")
121 {
122 println!(" DiceError::ParseError:");
123 println!(" - kind: {:?}", kind);
124 println!(" - input: {}", input);
125 println!(" - start: {}", start);
126 }
127
128 // SyntaxError variant
129 if let Err(DiceError::SyntaxError { expected, .. }) = DiceExpr::parse("-2d6") {
130 println!("\n DiceError::SyntaxError:");
131 if let Some(exp) = expected {
132 println!(" - expected: {}", exp);
133 }
134 }
135
136 // TrailingInput variant
137 if let Err(DiceError::TrailingInput { pos, .. }) = DiceExpr::parse("2d6 extra") {
138 println!("\n DiceError::TrailingInput:");
139 println!(" - pos: {}", pos);
140 }
141
142 // InvalidSpec variant
143 let bad_spec = RollSpec::new(2, 6, Some(Keep::Highest(5)));
144 if let Err(DiceError::InvalidSpec(spec, msg)) = DiceExpr::Roll(bad_spec).roll() {
145 println!("\n DiceError::InvalidSpec:");
146 println!(" - spec: {:?}", spec);
147 println!(" - message: {}", msg);
148 }
149
150 // ========================================
151 // 6. Complete Example
152 // ========================================
153 println!("\n6. Complete Example:");
154 println!(" Building a complex expression with all features\n");
155
156 // Character creation: 6 abilities, each 4d6 keep highest 3
157 println!(" Rolling D&D ability scores (4d6 drop lowest):");
158 let ability_roll = DiceExpr::Roll(RollSpec::new(4, 6, Some(Keep::Highest(3))));
159
160 for i in 1..=6 {
161 if let Ok(result) = ability_roll.roll() {
162 println!(
163 " Ability {}: {} (rolls: {:?})",
164 i, result.total, result.rolls
165 );
166 }
167 }
168}