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#[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#[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#[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#[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#[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#[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
229enum 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}