mtg_data/
mana_cost.rs

1#[derive(idris_derive::Idris)]
2#[derive(serde::Serialize, serde::Deserialize)]
3#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
4#[cfg_attr(feature = "ts_export", derive(ts_rs::TS))]
5pub enum Mana {
6    X,
7    Any(AnyMana),
8    Colored(ColoredMana),
9    Hybrid(HybridMana),
10    MonocoloredHybrid(MonocoloredHybridMana),
11    Phyrexian(PhyrexianMana),
12    HybridPhyrexian(HybridPhyrexianMana),
13    Snow,
14}
15
16impl Mana {
17    pub fn mana_value(&self) -> usize {
18        match self {
19            Mana::X => 0,
20            Mana::Any(any_mana) => any_mana.number,
21            Mana::Colored(_) => 1,
22            Mana::Hybrid(_) => 1,
23            Mana::MonocoloredHybrid(hybrid_mana) => 1.max(hybrid_mana.number),
24            Mana::Phyrexian(_) => 1,
25            Mana::HybridPhyrexian(_) => 1,
26            Mana::Snow => 1,
27        }
28    }
29}
30
31impl std::str::FromStr for Mana {
32    type Err = String;
33    fn from_str(from: &str) -> Result<Self, Self::Err> {
34        if from.starts_with('{') && from.ends_with('}') {
35            let symbols = from[1..from.len() - 1]
36                .split('/')
37                .map(ManaSymbol::parse_symbol)
38                .collect::<Result<Vec<_>, _>>()?;
39            match symbols.as_slice() {
40                [ManaSymbol::X] => Ok(Mana::X),
41                [ManaSymbol::Any(number)] => Ok(Mana::Any(AnyMana { number: *number })),
42                [ManaSymbol::Colored(color)] => Ok(Mana::Colored(ColoredMana { color: *color })),
43                [ManaSymbol::Colored(c1), ManaSymbol::Colored(c2)] => Ok(Mana::Hybrid(HybridMana {
44                    color_1: *c1,
45                    color_2: *c2,
46                })),
47                [ManaSymbol::Any(number), ManaSymbol::Colored(color)] => Ok(Mana::MonocoloredHybrid(MonocoloredHybridMana {
48                    number: *number,
49                    color: *color,
50                })),
51                [ManaSymbol::Colored(color), ManaSymbol::Phyrexian] => Ok(Mana::Phyrexian(PhyrexianMana { color: *color })),
52                [ManaSymbol::Colored(c1), ManaSymbol::Colored(c2), ManaSymbol::Phyrexian] => {
53                    Ok(Mana::HybridPhyrexian(HybridPhyrexianMana {
54                        color_1: *c1,
55                        color_2: *c2,
56                    }))
57                }
58                [ManaSymbol::Snow] => Ok(Mana::Snow),
59                _ => Err(format!("Invalid symbol combination: {symbols:?}")),
60            }
61        } else {
62            Err(format!("Mana cost shall be between curly braces, got {from}"))
63        }
64    }
65}
66
67impl std::fmt::Display for Mana {
68    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
69        match self {
70            Mana::X => write!(f, "{{x}}"),
71            Mana::Snow => write!(f, "{{s}}"),
72            Mana::Any(mana) => write!(f, "{mana}"),
73            Mana::Colored(mana) => write!(f, "{mana}"),
74            Mana::Hybrid(mana) => write!(f, "{mana}"),
75            Mana::MonocoloredHybrid(mana) => write!(f, "{mana}"),
76            Mana::Phyrexian(mana) => write!(f, "{mana}"),
77            Mana::HybridPhyrexian(mana) => write!(f, "{mana}"),
78        }
79    }
80}
81
82/// A mana symbol with a number on it, representing a fixed amount of any kind of mana.
83#[derive(serde::Serialize, serde::Deserialize)]
84#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
85#[cfg_attr(feature = "ts_export", derive(ts_rs::TS))]
86pub struct AnyMana {
87    pub number: usize,
88}
89
90impl idris::Idris for AnyMana {
91    const COUNT: usize = 1;
92    fn id(&self) -> usize {
93        0
94    }
95    fn name_from_id(_: usize) -> &'static str {
96        "{number}"
97    }
98}
99
100impl std::fmt::Display for AnyMana {
101    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
102        write!(f, "{{{}}}", self.number)
103    }
104}
105
106/// A mana symbol with a number on it, representing a fixed amount of any kind of mana.
107#[derive(serde::Serialize, serde::Deserialize)]
108#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
109#[cfg_attr(feature = "ts_export", derive(ts_rs::TS))]
110pub struct ColoredMana {
111    pub color: crate::Color,
112}
113
114impl idris::Idris for ColoredMana {
115    const COUNT: usize = 1;
116    fn id(&self) -> usize {
117        0
118    }
119    fn name_from_id(_: usize) -> &'static str {
120        "{color}"
121    }
122}
123
124impl std::fmt::Display for ColoredMana {
125    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
126        write!(f, "{{{}}}", self.color.as_char())
127    }
128}
129
130/// A mana symbol with a number on it, representing a fixed amount of any kind of mana.
131#[derive(serde::Serialize, serde::Deserialize)]
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
133#[cfg_attr(feature = "ts_export", derive(ts_rs::TS))]
134pub struct HybridMana {
135    pub color_1: crate::Color,
136    pub color_2: crate::Color,
137}
138
139impl idris::Idris for HybridMana {
140    const COUNT: usize = 1;
141    fn id(&self) -> usize {
142        0
143    }
144    fn name_from_id(_: usize) -> &'static str {
145        "{color/color}"
146    }
147}
148
149impl std::fmt::Display for HybridMana {
150    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
151        write!(f, "{{{}/{}}}", self.color_1.as_char(), self.color_2.as_char())
152    }
153}
154
155/// A mana symbol with a number on it, representing a fixed amount of any kind of mana.
156#[derive(serde::Serialize, serde::Deserialize)]
157#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
158#[cfg_attr(feature = "ts_export", derive(ts_rs::TS))]
159pub struct MonocoloredHybridMana {
160    pub color: crate::Color,
161    pub number: usize,
162}
163
164impl idris::Idris for MonocoloredHybridMana {
165    const COUNT: usize = 1;
166    fn id(&self) -> usize {
167        0
168    }
169    fn name_from_id(_: usize) -> &'static str {
170        "{number/color}"
171    }
172}
173
174impl std::fmt::Display for MonocoloredHybridMana {
175    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
176        write!(f, "{{{}/{}}}", self.number, self.color.as_char())
177    }
178}
179
180/// A mana symbol with a number on it, representing a fixed amount of any kind of mana.
181#[derive(serde::Serialize, serde::Deserialize)]
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
183#[cfg_attr(feature = "ts_export", derive(ts_rs::TS))]
184pub struct PhyrexianMana {
185    pub color: crate::Color,
186}
187
188impl idris::Idris for PhyrexianMana {
189    const COUNT: usize = 1;
190    fn id(&self) -> usize {
191        0
192    }
193    fn name_from_id(_: usize) -> &'static str {
194        "{color/p}"
195    }
196}
197
198impl std::fmt::Display for PhyrexianMana {
199    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
200        write!(f, "{{{}/p}}", self.color.as_char())
201    }
202}
203
204/// A mana symbol with a number on it, representing a fixed amount of any kind of mana.
205#[derive(serde::Serialize, serde::Deserialize)]
206#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord)]
207#[cfg_attr(feature = "ts_export", derive(ts_rs::TS))]
208pub struct HybridPhyrexianMana {
209    pub color_1: crate::Color,
210    pub color_2: crate::Color,
211}
212
213impl idris::Idris for HybridPhyrexianMana {
214    const COUNT: usize = 1;
215    fn id(&self) -> usize {
216        0
217    }
218    fn name_from_id(_: usize) -> &'static str {
219        "{color/color/p}"
220    }
221}
222
223impl std::fmt::Display for HybridPhyrexianMana {
224    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
225        write!(f, "{{{}/{}/p}}", self.color_1.as_char(), self.color_2.as_char())
226    }
227}
228
229/// Inner type used to parse mana costs.
230enum ManaSymbol {
231    X,
232    Any(usize),
233    Colored(crate::Color),
234    Phyrexian,
235    Snow,
236}
237
238impl std::fmt::Debug for ManaSymbol {
239    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
240        match self {
241            ManaSymbol::X => write!(f, "x"),
242            ManaSymbol::Any(num) => write!(f, "{num}"),
243            ManaSymbol::Colored(color) => write!(f, "{}", color.as_char()),
244            ManaSymbol::Phyrexian => write!(f, "p"),
245            ManaSymbol::Snow => write!(f, "s"),
246        }
247    }
248}
249
250impl ManaSymbol {
251    fn parse_symbol(input: &str) -> Result<ManaSymbol, String> {
252        return match input {
253            "w" | "W" => Ok(ManaSymbol::Colored(crate::Color::White)),
254            "b" | "B" => Ok(ManaSymbol::Colored(crate::Color::Black)),
255            "r" | "R" => Ok(ManaSymbol::Colored(crate::Color::Red)),
256            "u" | "U" => Ok(ManaSymbol::Colored(crate::Color::Blue)),
257            "g" | "G" => Ok(ManaSymbol::Colored(crate::Color::Green)),
258            "c" | "C" => Ok(ManaSymbol::Colored(crate::Color::Colorless)),
259            "x" | "X" => Ok(ManaSymbol::X),
260            "s" | "S" => Ok(ManaSymbol::Snow),
261            "p" | "P" => Ok(ManaSymbol::Phyrexian),
262            other => match other.parse() {
263                Ok(num) => Ok(ManaSymbol::Any(num)),
264                Err(_) => Err(format!("Unknown mana symbol: {other}")),
265            },
266        };
267    }
268}