Skip to main content

DiceExpr

Enum DiceExpr 

Source
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

Source

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 expression
  • Err(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?
examples/error_handling.rs (line 37)
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
Hide additional examples
examples/basic_usage.rs (line 8)
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}
examples/complete_api.rs (line 38)
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}
Source

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 the rand::Rng trait
§Returns
  • Ok(ExprResult): The result of evaluating the expression
  • Err(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?
examples/custom_rng.rs (line 15)
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
Hide additional examples
examples/complete_api.rs (line 51)
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}
Source

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: NdS where 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 expression
  • Err(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?
examples/custom_rng.rs (line 9)
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
Hide additional examples
examples/error_handling.rs (line 10)
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}
examples/basic_usage.rs (line 7)
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}
examples/complete_api.rs (line 15)
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}

Trait Implementations§

Source§

impl Clone for DiceExpr

Source§

fn clone(&self) -> DiceExpr

Returns a duplicate of the value. Read more
1.0.0 · Source§

fn clone_from(&mut self, source: &Self)

Performs copy-assignment from source. Read more
Source§

impl Debug for DiceExpr

Source§

fn fmt(&self, f: &mut Formatter<'_>) -> Result

Formats the value using the given formatter. Read more
Source§

impl FromStr for DiceExpr

Source§

type Err = DiceError

The associated error which can be returned from parsing.
Source§

fn from_str(s: &str) -> Result<Self, Self::Err>

Parses a string s to return a value of this type. Read more

Auto Trait Implementations§

Blanket Implementations§

Source§

impl<T> Any for T
where T: 'static + ?Sized,

Source§

fn type_id(&self) -> TypeId

Gets the TypeId of self. Read more
Source§

impl<T> Borrow<T> for T
where T: ?Sized,

Source§

fn borrow(&self) -> &T

Immutably borrows from an owned value. Read more
Source§

impl<T> BorrowMut<T> for T
where T: ?Sized,

Source§

fn borrow_mut(&mut self) -> &mut T

Mutably borrows from an owned value. Read more
Source§

impl<T> CloneToUninit for T
where T: Clone,

Source§

unsafe fn clone_to_uninit(&self, dest: *mut u8)

🔬This is a nightly-only experimental API. (clone_to_uninit)
Performs copy-assignment from self to dest. Read more
Source§

impl<T> From<T> for T

Source§

fn from(t: T) -> T

Returns the argument unchanged.

Source§

impl<T, U> Into<U> for T
where U: From<T>,

Source§

fn into(self) -> U

Calls U::from(self).

That is, this conversion is whatever the implementation of From<T> for U chooses to do.

Source§

impl<T> ToOwned for T
where T: Clone,

Source§

type Owned = T

The resulting type after obtaining ownership.
Source§

fn to_owned(&self) -> T

Creates owned data from borrowed data, usually by cloning. Read more
Source§

fn clone_into(&self, target: &mut T)

Uses borrowed data to replace owned data, usually by cloning. Read more
Source§

impl<T, U> TryFrom<U> for T
where U: Into<T>,

Source§

type Error = Infallible

The type returned in the event of a conversion error.
Source§

fn try_from(value: U) -> Result<T, <T as TryFrom<U>>::Error>

Performs the conversion.
Source§

impl<T, U> TryInto<U> for T
where U: TryFrom<T>,

Source§

type Error = <U as TryFrom<T>>::Error

The type returned in the event of a conversion error.
Source§

fn try_into(self) -> Result<U, <U as TryFrom<T>>::Error>

Performs the conversion.
Source§

impl<V, T> VZip<V> for T
where V: MultiLane<T>,

Source§

fn vzip(self) -> V