1use core::fmt;
2use std::str::FromStr;
3
4use crate::{numpad, CreationError};
5
6#[derive(Debug, Clone, PartialEq, Eq)]
8pub struct Move {
9 button: Button,
10 motion: Motion,
11 modifier: Modifier,
12}
13
14#[derive(Debug, Clone, PartialEq, Eq)]
16pub struct Button(String);
17
18#[non_exhaustive]
20#[derive(Debug, Clone, PartialEq, Eq)]
21pub enum Motion {
22 N,
23 U,
24 D,
25 B,
26 F,
27 DB,
28 DF,
29 UB,
30 UF,
31 QCF,
32 QCB,
33 HCF,
34 HCB,
35 DP,
36 RDP,
37 FullCircle,
38 Double360,
39 Other(String),
40}
41
42#[non_exhaustive]
44#[derive(Debug, Clone, Copy, PartialEq, Eq)]
45pub enum Modifier {
46 Close,
47 Far,
48 Standing,
49 Crouching,
50 Jump,
51 SuperJump,
52 JumpCancel,
53 TigerKnee,
54 None,
55}
56
57impl Move {
58 pub fn new<S>(input: S) -> Result<Self, CreationError>
63 where
64 S: ToString,
65 {
66 let mut input = input.to_string().trim().to_string();
67 let modifier = Self::get_modifier(&mut input)?;
68 let input = input.split_whitespace().collect::<Vec<&str>>();
69 let motion = if input.len() > 1 {
70 Motion::new(input[0])
71 } else {
72 Motion::N
73 };
74 let button = Button::new(input.last().unwrap())?;
75
76 Ok(Self {
77 button,
78 motion,
79 modifier,
80 })
81 }
82
83 fn get_modifier(input: &mut String) -> Result<Modifier, CreationError> {
84 if input.contains('.') {
85 let prefix = input.chars().take_while(|c| *c != '.').collect::<String>();
86 for _ in 0..prefix.len() {
87 (*input).remove(0);
88 }
89 (*input).remove(0);
90 Ok(Modifier::new(prefix)?)
91 } else {
92 Ok(Modifier::None)
93 }
94 }
95
96 pub fn button(&self) -> Button {
97 self.button.clone()
98 }
99
100 pub fn motion(&self) -> Motion {
101 self.motion.clone()
102 }
103
104 pub fn modifier(&self) -> Modifier {
105 self.modifier
106 }
107}
108
109impl Button {
110 pub fn new<S>(b: S) -> Result<Self, CreationError>
116 where
117 S: ToString,
118 {
119 let b = b.to_string();
120 if !b.chars().all(|c| c.is_ascii_alphabetic()) {
121 Err(CreationError::InvalidButton)
122 } else {
123 Ok(Self(b))
124 }
125 }
126}
127
128impl Modifier {
129 pub fn new<S>(m: S) -> Result<Self, CreationError>
135 where
136 S: ToString,
137 {
138 let m = m.to_string();
139 match m.to_lowercase().as_str() {
140 "j." | "j" => Ok(Self::Jump),
141 "sj." | "sj" => Ok(Self::SuperJump),
142 "jc." | "jc" => Ok(Self::JumpCancel),
143 "cl." | "cl" => Ok(Self::Close),
144 "f." | "f" => Ok(Self::Far),
145 "tk." | "tk" => Ok(Self::TigerKnee),
146 "cr." | "cr" => Ok(Self::Crouching),
147 "st." | "st" => Ok(Self::Standing),
148 _ => Err(CreationError::InvalidModifier),
149 }
150 }
151}
152
153impl Motion {
154 pub fn new<S>(m: S) -> Self
157 where
158 S: ToString,
159 {
160 let m = m.to_string();
161 match m.to_lowercase().as_str() {
162 "n" => Self::N,
163 "u" => Self::U,
164 "d" => Self::D,
165 "b" => Self::B,
166 "f" => Self::F,
167 "ub" | "u/b" => Self::UB,
168 "uf" | "u/f" => Self::UF,
169 "db" | "d/b" => Self::DB,
170 "df" | "d/f" => Self::DF,
171 "qcf" => Self::QCF,
172 "qcb" => Self::QCB,
173 "hcf" => Self::HCF,
174 "hcb" => Self::HCB,
175 "360" => Self::FullCircle,
176 "720" => Self::Double360,
177 other => Self::Other(other.to_string()),
178 }
179 }
180}
181
182impl From<numpad::Move> for Move {
183 fn from(m: numpad::Move) -> Self {
184 let button = Button::from(m.button());
185 let m_motion = m.motion();
186 let motion = if m_motion.is_neutral() {
187 Motion::N
188 } else {
189 Motion::from(m_motion)
190 };
191 let modifier = Modifier::from(m.modifier());
192
193 Self {
194 button,
195 motion,
196 modifier,
197 }
198 }
199}
200
201impl FromStr for Move {
202 type Err = CreationError;
203
204 fn from_str(s: &str) -> Result<Self, Self::Err> {
205 Self::new(s)
206 }
207}
208
209impl fmt::Display for Move {
210 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
211 write!(
212 f,
213 "{}{}{}{}",
214 self.modifier,
215 self.motion,
216 if self.motion != Motion::N { " " } else { "" },
217 self.button
218 )
219 }
220}
221
222impl From<numpad::Motion> for Motion {
223 fn from(m: numpad::Motion) -> Self {
224 match m.to_string().as_str() {
225 "5" | "" => Self::N,
226 "8" => Self::U,
227 "2" => Self::D,
228 "4" => Self::B,
229 "6" => Self::F,
230 "7" => Self::UB,
231 "9" => Self::UF,
232 "1" => Self::DB,
233 "3" => Self::DF,
234 "236" => Self::QCF,
235 "214" => Self::QCB,
236 "41236" => Self::HCF,
237 "63214" => Self::HCB,
238 "41236987" => Self::FullCircle,
239 "4123698741236987" => Self::Double360,
240 other => Self::Other(other.to_string()),
241 }
242 }
243}
244
245impl FromStr for Motion {
246 type Err = CreationError;
247
248 fn from_str(s: &str) -> Result<Self, Self::Err> {
249 Ok(Self::new(s))
250 }
251}
252
253impl fmt::Display for Motion {
254 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
255 match self {
256 Motion::N => write!(f, ""),
257 Motion::U => write!(f, "U"),
258 Motion::D => write!(f, "D"),
259 Motion::B => write!(f, "B"),
260 Motion::F => write!(f, "F"),
261 Motion::DB => write!(f, "DB"),
262 Motion::DF => write!(f, "DF"),
263 Motion::UB => write!(f, "UB"),
264 Motion::UF => write!(f, "UF"),
265 Motion::QCF => write!(f, "QCF"),
266 Motion::QCB => write!(f, "QCB"),
267 Motion::HCF => write!(f, "HCF"),
268 Motion::HCB => write!(f, "HCB"),
269 Motion::DP => write!(f, "DP"),
270 Motion::RDP => write!(f, "RDP"),
271 Motion::FullCircle => write!(f, "360"),
272 Motion::Double360 => write!(f, "720"),
273 Motion::Other(o) => write!(f, "'{o}'"),
274 }
275 }
276}
277
278impl From<numpad::Modifier> for Modifier {
279 fn from(m: numpad::Modifier) -> Self {
280 match m {
281 numpad::Modifier::Jump => Self::Jump,
282 numpad::Modifier::SuperJump => Self::SuperJump,
283 numpad::Modifier::JumpCancel => Self::JumpCancel,
284 numpad::Modifier::Close => Self::Close,
285 numpad::Modifier::Far => Self::Far,
286 numpad::Modifier::TigerKnee => Self::TigerKnee,
287 numpad::Modifier::None => Self::None,
288 }
289 }
290}
291
292impl FromStr for Modifier {
293 type Err = CreationError;
294
295 fn from_str(s: &str) -> Result<Self, Self::Err> {
296 Self::new(s)
297 }
298}
299
300impl fmt::Display for Modifier {
301 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
302 let prefix = match self {
303 Modifier::Close => "cl.",
304 Modifier::Far => "f.",
305 Modifier::Standing => "st.",
306 Modifier::Crouching => "cr.",
307 Modifier::Jump => "j.",
308 Modifier::SuperJump => "sj.",
309 Modifier::JumpCancel => "jc.",
310 Modifier::TigerKnee => "tk.",
311 Modifier::None => "",
312 };
313 write!(f, "{prefix}")
314 }
315}
316
317impl From<numpad::Button> for Button {
318 fn from(b: numpad::Button) -> Self {
319 Self(b.to_string())
320 }
321}
322
323impl FromStr for Button {
324 type Err = CreationError;
325
326 fn from_str(s: &str) -> Result<Self, Self::Err> {
327 Self::new(s)
328 }
329}
330
331impl fmt::Display for Button {
332 fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
333 write!(f, "{}", self.0)
334 }
335}
336
337#[cfg(test)]
338mod tests {
339 use super::*;
340 use pretty_assertions::assert_eq;
341
342 #[test]
343 fn qcf_hp() {
344 let attack = "qcf HP";
345 let created = Move::new(attack).unwrap();
346
347 assert_eq!(
348 created,
349 Move {
350 button: Button("HP".to_string()),
351 motion: Motion::QCF,
352 modifier: Modifier::None
353 }
354 )
355 }
356
357 #[test]
358 fn cr_mk() {
359 let attack = "cr.mk";
360 let created = Move::new(attack).unwrap();
361
362 assert_eq!(
363 created,
364 Move {
365 button: Button("mk".to_string()),
366 motion: Motion::N,
367 modifier: Modifier::Crouching
368 },
369 )
370 }
371
372 #[test]
373 fn tk_qcf_hk() {
374 let attack = "tk.qcf HK";
375 let created = Move::new(attack).unwrap();
376
377 assert_eq!(
378 created,
379 Move {
380 button: Button("HK".to_string()),
381 motion: Motion::QCF,
382 modifier: Modifier::TigerKnee
383 }
384 )
385 }
386}