rdice_core/die.rs
1//! Dice value objects.
2//!
3//! This module contains the serializable representation of dice and die faces.
4//! Built-in dice use numeric faces; custom dice may mix numeric and text faces.
5
6use serde::{Deserialize, Serialize};
7
8/// A single face on a die.
9///
10/// Integer faces contribute their value to roll sums and point analysis. Text
11/// faces render as text and contribute zero points during analysis.
12#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)]
13#[serde(tag = "type", content = "value")]
14pub enum FaceValue {
15 /// A numeric face value.
16 #[serde(rename = "integer")]
17 Integer(i64),
18 /// A text face value.
19 #[serde(rename = "text")]
20 Text(String),
21}
22
23impl std::fmt::Display for FaceValue {
24 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
25 match self {
26 FaceValue::Integer(n) => write!(f, "{n}"),
27 FaceValue::Text(s) => write!(f, "{s}"),
28 }
29 }
30}
31
32/// Classifies whether a die is provided by the crate or supplied by a caller.
33#[derive(Debug, Clone, Copy, PartialEq, Default, Serialize, Deserialize)]
34pub enum DieKind {
35 /// One of the built-in numeric dice.
36 Builtin,
37 /// A caller-defined die.
38 #[default]
39 Custom,
40}
41
42/// A die with a stable name and ordered set of faces.
43#[derive(Debug, Clone, Serialize, Deserialize)]
44pub struct Die {
45 /// Canonical die name.
46 ///
47 /// Built-in dice are named `D4`, `D6`, `D8`, `D10`, `D12`, `D20`, and
48 /// `D100`. Custom dice are stored with [`CUSTOM_PREFIX`].
49 pub name: String,
50 /// Ordered set of faces that may be rolled.
51 pub faces: Vec<FaceValue>,
52 /// Whether this die is built-in or custom.
53 #[serde(default)]
54 pub kind: DieKind,
55}
56
57/// Prefix used for canonical custom die names.
58pub const CUSTOM_PREFIX: &str = "\u{273d}";
59
60/// Returns the built-in numeric dice.
61///
62/// The built-in set contains `D4`, `D6`, `D8`, `D10`, `D12`, `D20`, and
63/// `D100`.
64pub fn builtin_dice() -> Vec<Die> {
65 [4, 6, 8, 10, 12, 20, 100]
66 .into_iter()
67 .map(|n| Die {
68 name: format!("D{n}"),
69 faces: (1..=n).map(|i| FaceValue::Integer(i as i64)).collect(),
70 kind: DieKind::Builtin,
71 })
72 .collect()
73}