1#[cfg(feature = "chumsky")]
4use chumsky::{
5 Parser,
6 prelude::{choice, just},
7};
8
9#[derive(
11 Debug,
12 Clone,
13 Hash,
14 PartialEq,
15 Eq,
16 strum::FromRepr,
17 strum::EnumIs,
18 serde::Serialize,
19 serde::Deserialize,
20)]
21pub enum AvatarAttachmentPoint {
22 Skull = 2,
24 Nose = 17,
26 Mouth = 11,
28 Tongue = 52,
30 Chin = 12,
32 Jaw = 47,
34 LeftEar = 13,
36 RightEar = 14,
38 AltLeftEar = 48,
40 AltRightEar = 49,
42 LeftEye = 15,
44 RightEye = 16,
46 AltLeftEye = 50,
48 AltRightEye = 51,
50 Neck = 39,
52 LeftShoulder = 3,
54 RightShoulder = 4,
56 LeftUpperArm = 20,
58 RightUpperArm = 18,
60 LeftLowerArm = 21,
62 RightLowerArm = 19,
64 LeftHand = 5,
66 RightHand = 6,
68 LeftRingFinger = 41,
70 RightRingFinger = 42,
72 LeftWing = 45,
74 RightWing = 46,
76 Chest = 1,
78 LeftPec = 29,
80 RightPec = 30,
82 Stomach = 28,
84 Spine = 9,
86 TailBase = 43,
88 TailTip = 44,
90 AvatarCenter = 40,
92 Pelvis = 10,
94 Groin = 53,
96 LeftHip = 25,
98 RightHip = 22,
100 LeftUpperLeg = 26,
102 RightUpperLeg = 23,
104 LeftLowerLeg = 24,
106 RightLowerLeg = 27,
108 LeftFoot = 7,
110 RightFoot = 8,
112 LeftHindFoot = 54,
114 RightHindFoot = 55,
116}
117
118impl AvatarAttachmentPoint {
119 #[must_use]
121 pub const fn requires_bento(&self) -> bool {
122 matches!(
123 self,
124 Self::Tongue
125 | Self::AltLeftEar
126 | Self::AltRightEar
127 | Self::AltLeftEye
128 | Self::AltRightEye
129 | Self::LeftRingFinger
130 | Self::RightRingFinger
131 | Self::LeftWing
132 | Self::RightWing
133 | Self::TailBase
134 | Self::TailTip
135 | Self::Groin
136 | Self::LeftHindFoot
137 | Self::RightHindFoot
138 )
139 }
140}
141
142impl std::fmt::Display for AvatarAttachmentPoint {
143 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
144 match self {
145 Self::Skull => write!(f, "Skull"),
146 Self::Nose => write!(f, "Nose"),
147 Self::Mouth => write!(f, "Mouth"),
148 Self::Tongue => write!(f, "Tongue"),
149 Self::Chin => write!(f, "Chin"),
150 Self::Jaw => write!(f, "Jaw"),
151 Self::LeftEar => write!(f, "Left Ear"),
152 Self::RightEar => write!(f, "Right Ear"),
153 Self::AltLeftEar => write!(f, "Alt Left Ear"),
154 Self::AltRightEar => write!(f, "Alt Right Ear"),
155 Self::LeftEye => write!(f, "Left Eye"),
156 Self::RightEye => write!(f, "Right Eye"),
157 Self::AltLeftEye => write!(f, "Alt Left Eye"),
158 Self::AltRightEye => write!(f, "Alt Right Eye"),
159 Self::Neck => write!(f, "Neck"),
160 Self::LeftShoulder => write!(f, "Left Shoulder"),
161 Self::RightShoulder => write!(f, "Right Shoulder"),
162 Self::LeftUpperArm => write!(f, "L Upper Arm"),
163 Self::RightUpperArm => write!(f, "R Upper Arm"),
164 Self::LeftLowerArm => write!(f, "L Lower Arm"),
165 Self::RightLowerArm => write!(f, "R Lower Arm"),
166 Self::LeftHand => write!(f, "Left Hand"),
167 Self::RightHand => write!(f, "Right Hand"),
168 Self::LeftRingFinger => write!(f, "Left Ring Finger"),
169 Self::RightRingFinger => write!(f, "Right Ring Finger"),
170 Self::LeftWing => write!(f, "Left Wing"),
171 Self::RightWing => write!(f, "Right Wing"),
172 Self::Chest => write!(f, "Chest"),
173 Self::LeftPec => write!(f, "Left Pec"),
174 Self::RightPec => write!(f, "Right Pec"),
175 Self::Stomach => write!(f, "Stomach"),
176 Self::Spine => write!(f, "Spine"),
177 Self::TailBase => write!(f, "Tail Base"),
178 Self::TailTip => write!(f, "Tail Tip"),
179 Self::AvatarCenter => write!(f, "Avatar Center"),
180 Self::Pelvis => write!(f, "Pelvis"),
181 Self::Groin => write!(f, "Groin"),
182 Self::LeftHip => write!(f, "Left Hip"),
183 Self::RightHip => write!(f, "Right Hip"),
184 Self::LeftUpperLeg => write!(f, "L Upper Leg"),
185 Self::RightUpperLeg => write!(f, "R Upper Leg"),
186 Self::LeftLowerLeg => write!(f, "L Lower Leg"),
187 Self::RightLowerLeg => write!(f, "R Lower Leg"),
188 Self::LeftFoot => write!(f, "Left Foot"),
189 Self::RightFoot => write!(f, "Right Foot"),
190 Self::LeftHindFoot => write!(f, "Left Hind Foot"),
191 Self::RightHindFoot => write!(f, "Right Hind Foot"),
192 }
193 }
194}
195
196#[derive(Debug, Clone)]
198pub struct AvatarAttachmentPointParseError {
199 value: String,
201}
202
203impl std::fmt::Display for AvatarAttachmentPointParseError {
204 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
205 write!(
206 f,
207 "Could not parse as AvatarAttachmentPoint: {}",
208 self.value
209 )
210 }
211}
212
213impl std::str::FromStr for AvatarAttachmentPoint {
214 type Err = AvatarAttachmentPointParseError;
215
216 fn from_str(s: &str) -> Result<Self, Self::Err> {
217 match s {
218 "ATTACH_HEAD" | "Skull" | "head" => Ok(Self::Skull),
219 "ATTACH_NOSE" | "Nose" | "nose" => Ok(Self::Nose),
220 "ATTACH_MOUTH" | "Mouth" | "mouth" => Ok(Self::Mouth),
221 "ATTACH_FACE_TONGUE" | "Tongue" | "tongue" => Ok(Self::Tongue),
222 "ATTACH_CHIN" | "Chin" | "chin" => Ok(Self::Chin),
223 "ATTACH_FACE_JAW" | "Jaw" | "jaw" => Ok(Self::Jaw),
224 "ATTACH_LEAR" | "Left Ear" | "left ear" => Ok(Self::LeftEar),
225 "ATTACH_REAR" | "Right Ear" | "right ear" => Ok(Self::RightEar),
226 "ATTACH_FACE_LEAR" | "Alt Left Ear" | "left ear (extended)" => Ok(Self::AltLeftEar),
227 "ATTACH_FACE_REAR" | "Alt Right Ear" | "right ear (extended)" => Ok(Self::AltRightEar),
228 "ATTACH_LEYE" | "Left Eye" | "left eye" => Ok(Self::LeftEye),
229 "ATTACH_REYE" | "Right Eye" | "right eye" => Ok(Self::RightEye),
230 "ATTACH_FACE_LEYE" | "Alt Left Eye" | "left eye (extended)" => Ok(Self::AltLeftEye),
231 "ATTACH_FACE_REYE" | "Alt Right Eye" | "right eye (extended)" => Ok(Self::AltRightEye),
232 "ATTACH_NECK" | "Neck" | "neck" => Ok(Self::Neck),
233 "ATTACH_LSHOULDER" | "Left Shoulder" | "left shoulder" => Ok(Self::LeftShoulder),
234 "ATTACH_RSHOULDER" | "Right Shoulder" | "right shoulder" => Ok(Self::RightShoulder),
235 "ATTACH_LUARM" | "L Upper Arm" | "left upper arm" => Ok(Self::LeftUpperArm),
236 "ATTACH_RUARM" | "R Upper Arm" | "right upper arm" => Ok(Self::RightUpperArm),
237 "ATTACH_LLARM" | "L Lower Arm" | "left lower arm" => Ok(Self::LeftLowerArm),
238 "ATTACH_RLARM" | "R Lower Arm" | "right lower arm" => Ok(Self::RightLowerArm),
239 "ATTACH_LHAND" | "Left Hand" | "left hand" => Ok(Self::LeftHand),
240 "ATTACH_RHAND" | "Right Hand" | "right hand" => Ok(Self::RightHand),
241 "ATTACH_LHAND_RING1" | "Left Ring Finger" | "left ring finger" => {
242 Ok(Self::LeftRingFinger)
243 }
244 "ATTACH_RHAND_RING1" | "Right Ring Finger" | "right ring finger" => {
245 Ok(Self::RightRingFinger)
246 }
247 "ATTACH_LWING" | "Left Wing" | "left wing" => Ok(Self::LeftWing),
248 "ATTACH_RWING" | "Right Wing" | "right wing" => Ok(Self::RightWing),
249 "ATTACH_CHEST" | "Chest" | "chest/sternum" | "chest" | "sternum" => Ok(Self::Chest),
250 "ATTACH_LEFT_PEC" | "Left Pec" | "left pectoral" => Ok(Self::LeftPec),
251 "ATTACH_RIGHT_PEC" | "Right Pec" | "right pectoral" => Ok(Self::RightPec),
252 "ATTACH_BELLY" | "Stomach" | "belly/stomach/tummy" | "belly" | "stomach" | "tummy" => {
253 Ok(Self::Stomach)
254 }
255 "ATTACH_BACK" | "Spine" | "back" => Ok(Self::Spine),
256 "ATTACH_TAIL_BASE" | "Tail Base" | "tail base" => Ok(Self::TailBase),
257 "ATTACH_TAIL_TIP" | "Tail Tip" | "tail tip" => Ok(Self::TailTip),
258 "ATTACH_AVATAR_CENTER"
259 | "Avatar Center"
260 | "avatar center/root"
261 | "avatar center"
262 | "root" => Ok(Self::AvatarCenter),
263 "ATTACH_PELVIS" | "Pelvis" | "pelvis" => Ok(Self::Pelvis),
264 "ATTACH_GROIN" | "Groin" | "groin" => Ok(Self::Groin),
265 "ATTACH_LHIP" | "Left Hip" | "left hip" => Ok(Self::LeftHip),
266 "ATTACH_RHIP" | "Right Hip" | "right hip" => Ok(Self::RightHip),
267 "ATTACH_LULEG" | "L Upper Leg" | "left upper leg" => Ok(Self::LeftUpperLeg),
268 "ATTACH_RULEG" | "R Upper Leg" | "right upper leg" => Ok(Self::RightUpperLeg),
269 "ATTACH_RLLEG" | "R Lower Leg" | "right lower leg" => Ok(Self::LeftLowerLeg),
270 "ATTACH_LLLEG" | "L Lower Leg" | "left lower leg" => Ok(Self::RightLowerLeg),
271 "ATTACH_LFOOT" | "Left Foot" | "left foot" => Ok(Self::LeftFoot),
272 "ATTACH_RFOOT" | "Right Foot" | "right foot" => Ok(Self::RightFoot),
273 "ATTACH_HIND_LFOOT" | "Left Hind Foot" | "left hind foot" => Ok(Self::LeftHindFoot),
274 "ATTACH_HIND_RFOOT" | "Right Hind Foot" | "right hind foot" => Ok(Self::RightHindFoot),
275 _ => Err(AvatarAttachmentPointParseError {
276 value: s.to_string(),
277 }),
278 }
279 }
280}
281
282#[cfg(feature = "chumsky")]
288#[must_use]
289pub fn avatar_attachment_point_parser<'src>() -> impl Parser<
290 'src,
291 &'src str,
292 AvatarAttachmentPoint,
293 chumsky::extra::Err<chumsky::error::Rich<'src, char>>,
294> {
295 choice([
296 just("ATTACH_HEAD")
297 .or(just("Skull"))
298 .or(just("head"))
299 .to(AvatarAttachmentPoint::Skull)
300 .boxed(),
301 just("ATTACH_NOSE")
302 .or(just("Nose"))
303 .or(just("nose"))
304 .to(AvatarAttachmentPoint::Nose)
305 .boxed(),
306 just("ATTACH_MOUTH")
307 .or(just("Mouth"))
308 .or(just("mouth"))
309 .to(AvatarAttachmentPoint::Mouth)
310 .boxed(),
311 just("ATTACH_FACE_TONGUE")
312 .or(just("Tongue"))
313 .or(just("tongue"))
314 .to(AvatarAttachmentPoint::Tongue)
315 .boxed(),
316 just("ATTACH_CHIN")
317 .or(just("Chin"))
318 .or(just("chin"))
319 .to(AvatarAttachmentPoint::Chin)
320 .boxed(),
321 just("ATTACH_FACE_JAW")
322 .or(just("Jaw"))
323 .or(just("jaw"))
324 .to(AvatarAttachmentPoint::Jaw)
325 .boxed(),
326 just("ATTACH_LEAR")
327 .or(just("Left Ear"))
328 .or(just("left ear"))
329 .to(AvatarAttachmentPoint::LeftEar)
330 .boxed(),
331 just("ATTACH_REAR")
332 .or(just("Right Ear"))
333 .or(just("right ear"))
334 .to(AvatarAttachmentPoint::RightEar)
335 .boxed(),
336 just("ATTACH_FACE_LEAR")
337 .or(just("Alt Left Ear"))
338 .or(just("left ear (extended)"))
339 .to(AvatarAttachmentPoint::AltLeftEar)
340 .boxed(),
341 just("ATTACH_FACE_REAR")
342 .or(just("Alt Right Ear"))
343 .or(just("right ear (extended)"))
344 .to(AvatarAttachmentPoint::AltRightEar)
345 .boxed(),
346 just("ATTACH_LEYE")
347 .or(just("Left Eye"))
348 .or(just("left eye"))
349 .to(AvatarAttachmentPoint::LeftEye)
350 .boxed(),
351 just("ATTACH_REYE")
352 .or(just("Right Eye"))
353 .or(just("right eye"))
354 .to(AvatarAttachmentPoint::RightEye)
355 .boxed(),
356 just("ATTACH_FACE_LEYE")
357 .or(just("Alt Left Eye"))
358 .or(just("left eye (extended)"))
359 .to(AvatarAttachmentPoint::AltLeftEye)
360 .boxed(),
361 just("ATTACH_FACE_REYE")
362 .or(just("Alt Right Eye"))
363 .or(just("right eye (extended)"))
364 .to(AvatarAttachmentPoint::AltRightEye)
365 .boxed(),
366 just("ATTACH_NECK")
367 .or(just("Neck"))
368 .or(just("neck"))
369 .to(AvatarAttachmentPoint::Neck)
370 .boxed(),
371 just("ATTACH_LSHOULDER")
372 .or(just("Left Shoulder"))
373 .or(just("left shoulder"))
374 .to(AvatarAttachmentPoint::LeftShoulder)
375 .boxed(),
376 just("ATTACH_RSHOULDER")
377 .or(just("Right Shoulder"))
378 .or(just("right shoulder"))
379 .to(AvatarAttachmentPoint::RightShoulder)
380 .boxed(),
381 just("ATTACH_LUARM")
382 .or(just("L Upper Arm"))
383 .or(just("left upper arm"))
384 .to(AvatarAttachmentPoint::LeftUpperArm)
385 .boxed(),
386 just("ATTACH_RUARM")
387 .or(just("R Upper Arm"))
388 .or(just("right upper arm"))
389 .to(AvatarAttachmentPoint::RightUpperArm)
390 .boxed(),
391 just("ATTACH_LLARM")
392 .or(just("L Lower Arm"))
393 .or(just("left lower arm"))
394 .to(AvatarAttachmentPoint::LeftLowerArm)
395 .boxed(),
396 just("ATTACH_RLARM")
397 .or(just("R Lower Arm"))
398 .or(just("right lower arm"))
399 .to(AvatarAttachmentPoint::RightLowerArm)
400 .boxed(),
401 just("ATTACH_LHAND")
402 .or(just("Left Hand"))
403 .or(just("left hand"))
404 .to(AvatarAttachmentPoint::LeftHand)
405 .boxed(),
406 just("ATTACH_RHAND")
407 .or(just("Right Hand"))
408 .or(just("right hand"))
409 .to(AvatarAttachmentPoint::RightHand)
410 .boxed(),
411 just("ATTACH_LHAND_RING1")
412 .or(just("Left Ring Finger"))
413 .or(just("left ring finger"))
414 .to(AvatarAttachmentPoint::LeftRingFinger)
415 .boxed(),
416 just("ATTACH_RHAND_RING1")
417 .or(just("Right Ring Finger"))
418 .or(just("right ring finger"))
419 .to(AvatarAttachmentPoint::RightRingFinger)
420 .boxed(),
421 just("ATTACH_LWING")
422 .or(just("Left Wing"))
423 .or(just("left wing"))
424 .to(AvatarAttachmentPoint::LeftWing)
425 .boxed(),
426 just("ATTACH_RWING")
427 .or(just("Right Wing"))
428 .or(just("right wing"))
429 .to(AvatarAttachmentPoint::RightWing)
430 .boxed(),
431 just("ATTACH_CHEST")
432 .or(just("Chest"))
433 .or(just("chest/sternum"))
434 .or(just("chest"))
435 .or(just("sternum"))
436 .to(AvatarAttachmentPoint::Chest)
437 .boxed(),
438 just("ATTACH_LEFT_PEC")
439 .or(just("Left Pec"))
440 .or(just("left pectoral"))
441 .to(AvatarAttachmentPoint::LeftPec)
442 .boxed(),
443 just("ATTACH_RIGHT_PEC")
444 .or(just("Right Pec"))
445 .or(just("right pectoral"))
446 .to(AvatarAttachmentPoint::RightPec)
447 .boxed(),
448 just("ATTACH_BELLY")
449 .or(just("Stomach"))
450 .or(just("belly/stomach/tummy"))
451 .or(just("belly"))
452 .or(just("stomach"))
453 .or(just("tummy"))
454 .to(AvatarAttachmentPoint::Stomach)
455 .boxed(),
456 just("ATTACH_BACK")
457 .or(just("Spine"))
458 .or(just("back"))
459 .to(AvatarAttachmentPoint::Spine)
460 .boxed(),
461 just("ATTACH_TAIL_BASE")
462 .or(just("Tail Base"))
463 .or(just("tail base"))
464 .to(AvatarAttachmentPoint::TailBase)
465 .boxed(),
466 just("ATTACH_TAIL_TIP")
467 .or(just("Tail Tip"))
468 .or(just("tail tip"))
469 .to(AvatarAttachmentPoint::TailTip)
470 .boxed(),
471 just("ATTACH_AVATAR_CENTER")
472 .or(just("Avatar Center"))
473 .or(just("avatar center/root"))
474 .or(just("avatar center"))
475 .or(just("root"))
476 .to(AvatarAttachmentPoint::AvatarCenter)
477 .boxed(),
478 just("ATTACH_PELVIS")
479 .or(just("Pelvis"))
480 .or(just("pelvis"))
481 .to(AvatarAttachmentPoint::Pelvis)
482 .boxed(),
483 just("ATTACH_GROIN")
484 .or(just("Groin"))
485 .or(just("groin"))
486 .to(AvatarAttachmentPoint::Groin)
487 .boxed(),
488 just("ATTACH_LHIP")
489 .or(just("Left Hip"))
490 .or(just("left hip"))
491 .to(AvatarAttachmentPoint::LeftHip)
492 .boxed(),
493 just("ATTACH_RHIP")
494 .or(just("Right Hip"))
495 .or(just("right hip"))
496 .to(AvatarAttachmentPoint::RightHip)
497 .boxed(),
498 just("ATTACH_LULEG")
499 .or(just("L Upper Leg"))
500 .or(just("left upper leg"))
501 .to(AvatarAttachmentPoint::LeftUpperLeg)
502 .boxed(),
503 just("ATTACH_RULEG")
504 .or(just("R Upper Leg"))
505 .or(just("right upper leg"))
506 .to(AvatarAttachmentPoint::RightUpperLeg)
507 .boxed(),
508 just("ATTACH_RLLEG")
509 .or(just("R Lower Leg"))
510 .or(just("right lower leg"))
511 .to(AvatarAttachmentPoint::LeftLowerLeg)
512 .boxed(),
513 just("ATTACH_LLLEG")
514 .or(just("L Lower Leg"))
515 .or(just("left lower leg"))
516 .to(AvatarAttachmentPoint::RightLowerLeg)
517 .boxed(),
518 just("ATTACH_LFOOT")
519 .or(just("Left Foot"))
520 .or(just("left foot"))
521 .to(AvatarAttachmentPoint::LeftFoot)
522 .boxed(),
523 just("ATTACH_RFOOT")
524 .or(just("Right Foot"))
525 .or(just("right foot"))
526 .to(AvatarAttachmentPoint::RightFoot)
527 .boxed(),
528 just("ATTACH_HIND_LFOOT")
529 .or(just("Left Hind Foot"))
530 .or(just("left hind foot"))
531 .to(AvatarAttachmentPoint::LeftHindFoot)
532 .boxed(),
533 just("ATTACH_HIND_RFOOT")
534 .or(just("Right Hind Foot"))
535 .or(just("right hind foot"))
536 .to(AvatarAttachmentPoint::RightHindFoot)
537 .boxed(),
538 ])
539}
540
541#[derive(
543 Debug,
544 Clone,
545 Hash,
546 PartialEq,
547 Eq,
548 strum::FromRepr,
549 strum::EnumIs,
550 serde::Serialize,
551 serde::Deserialize,
552)]
553pub enum HudAttachmentPoint {
554 Center2 = 31,
556 TopRight = 32,
558 Top = 33,
560 TopLeft = 34,
562 Center = 35,
564 BottomLeft = 36,
566 Bottom = 37,
568 BottomRight = 38,
570}
571
572impl std::fmt::Display for HudAttachmentPoint {
573 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
574 match self {
575 Self::Center2 => write!(f, "HUD Center 2"),
576 Self::TopRight => write!(f, "HUD Top Right"),
577 Self::Top => write!(f, "HUD Top"),
578 Self::TopLeft => write!(f, "HUD Top Left"),
579 Self::Center => write!(f, "HUD Center"),
580 Self::BottomLeft => write!(f, "HUD Bottom Left"),
581 Self::Bottom => write!(f, "HUD Bottom"),
582 Self::BottomRight => write!(f, "HUD Bottom Right"),
583 }
584 }
585}
586
587#[derive(Debug, Clone)]
589pub struct HudAttachmentPointParseError {
590 value: String,
592}
593
594impl std::fmt::Display for HudAttachmentPointParseError {
595 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
596 write!(f, "Could not parse as HudAttachmentPoint: {}", self.value)
597 }
598}
599
600impl std::str::FromStr for HudAttachmentPoint {
601 type Err = HudAttachmentPointParseError;
602
603 fn from_str(s: &str) -> Result<Self, Self::Err> {
604 match s {
605 "ATTACH_HUD_CENTER_2" | "HUD Center 2" | "Center 2" => Ok(Self::Center2),
606 "ATTACH_HUD_TOP_RIGHT" | "HUD Top Right" | "Top Right" => Ok(Self::TopRight),
607 "ATTACH_HUD_TOP_CENTER" | "HUD Top" | "Top" => Ok(Self::Top),
608 "ATTACH_HUD_TOP_LEFT" | "HUD Top Left" | "Top Left" => Ok(Self::TopLeft),
609 "ATTACH_HUD_CENTER_1" | "HUD Center" | "Center" => Ok(Self::Center),
610 "ATTACH_HUD_BOTTOM_LEFT" | "HUD Bottom Left" | "Bottom Left" => Ok(Self::BottomLeft),
611 "ATTACH_HUD_BOTTOM" | "HUD Bottom" | "Bottom" => Ok(Self::Bottom),
612 "ATTACH_HUD_BOTTOM_RIGHT" | "HUD Bottom Right " | "Bottom Right" => {
613 Ok(Self::BottomRight)
614 }
615 _ => Err(HudAttachmentPointParseError {
616 value: s.to_string(),
617 }),
618 }
619 }
620}
621
622#[cfg(feature = "chumsky")]
628#[must_use]
629pub fn hud_attachment_point_parser<'src>() -> impl Parser<
630 'src,
631 &'src str,
632 HudAttachmentPoint,
633 chumsky::extra::Err<chumsky::error::Rich<'src, char>>,
634> {
635 choice([
636 just("ATTACH_HUD_CENTER_2")
637 .or(just("HUD Center 2"))
638 .or(just("Center 2"))
639 .to(HudAttachmentPoint::Center2),
640 just("ATTACH_HUD_TOP_RIGHT")
641 .or(just("HUD Top Right"))
642 .or(just("Top Right"))
643 .to(HudAttachmentPoint::TopRight),
644 just("ATTACH_HUD_TOP_LEFT")
645 .or(just("HUD Top Left"))
646 .or(just("Top Left"))
647 .to(HudAttachmentPoint::TopLeft),
648 just("ATTACH_HUD_TOP_CENTER")
649 .or(just("HUD Top"))
650 .or(just("Top"))
651 .to(HudAttachmentPoint::Top),
652 just("ATTACH_HUD_CENTER_1")
653 .or(just("HUD Center"))
654 .or(just("Center"))
655 .to(HudAttachmentPoint::Center),
656 just("ATTACH_HUD_BOTTOM_LEFT")
657 .or(just("HUD Bottom Left"))
658 .or(just("Bottom Left"))
659 .to(HudAttachmentPoint::BottomLeft),
660 just("ATTACH_HUD_BOTTOM_RIGHT")
661 .or(just("HUD Bottom Right "))
662 .or(just("Bottom Right"))
663 .to(HudAttachmentPoint::BottomRight),
664 just("ATTACH_HUD_BOTTOM")
665 .or(just("HUD Bottom"))
666 .or(just("Bottom"))
667 .to(HudAttachmentPoint::Bottom),
668 ])
669}
670
671#[derive(Debug, Clone, Hash, PartialEq, Eq, serde::Serialize, serde::Deserialize)]
673#[expect(
674 clippy::module_name_repetitions,
675 reason = "the type is going to be used outside of the module"
676)]
677pub enum AttachmentPoint {
678 Avatar(AvatarAttachmentPoint),
680 Hud(HudAttachmentPoint),
682}
683
684impl AttachmentPoint {
685 #[must_use]
691 pub fn from_repr(repr: usize) -> Option<Self> {
692 AvatarAttachmentPoint::from_repr(repr)
693 .map(Self::Avatar)
694 .or_else(|| HudAttachmentPoint::from_repr(repr).map(Self::Hud))
695 }
696}
697
698impl std::fmt::Display for AttachmentPoint {
699 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
700 match self {
701 Self::Avatar(avatar_attachment_point) => {
702 write!(f, "{avatar_attachment_point}")
703 }
704 Self::Hud(hud_attachment_point) => write!(f, "{hud_attachment_point}"),
705 }
706 }
707}
708
709#[derive(Debug, Clone)]
711#[expect(
712 clippy::module_name_repetitions,
713 reason = "the error is going to be used outside of the module"
714)]
715pub struct AttachmentPointParseError {
716 value: String,
718}
719
720impl std::fmt::Display for AttachmentPointParseError {
721 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
722 write!(f, "Could not parse as AttachmentPoint: {}", self.value)
723 }
724}
725
726impl std::str::FromStr for AttachmentPoint {
727 type Err = AttachmentPointParseError;
728
729 fn from_str(s: &str) -> Result<Self, Self::Err> {
730 if let Ok(avatar_attachment_point) =
731 <AvatarAttachmentPoint as std::str::FromStr>::from_str(s)
732 {
733 Ok(Self::Avatar(avatar_attachment_point))
734 } else if let Ok(hud_attachment_point) =
735 <HudAttachmentPoint as std::str::FromStr>::from_str(s)
736 {
737 Ok(Self::Hud(hud_attachment_point))
738 } else {
739 Err(AttachmentPointParseError {
740 value: s.to_string(),
741 })
742 }
743 }
744}
745
746#[cfg(feature = "chumsky")]
752#[must_use]
753#[expect(
754 clippy::module_name_repetitions,
755 reason = "the parser is going to be used outside of the module"
756)]
757pub fn attachment_point_parser<'src>()
758-> impl Parser<'src, &'src str, AttachmentPoint, chumsky::extra::Err<chumsky::error::Rich<'src, char>>>
759{
760 avatar_attachment_point_parser()
761 .map(AttachmentPoint::Avatar)
762 .or(hud_attachment_point_parser().map(AttachmentPoint::Hud))
763}
764
765#[cfg(test)]
766mod test {
767 #[cfg(feature = "chumsky")]
768 use super::{AttachmentPoint, HudAttachmentPoint, attachment_point_parser};
769 #[cfg(feature = "chumsky")]
770 use chumsky::Parser as _;
771 #[cfg(feature = "chumsky")]
772 use pretty_assertions::assert_eq;
773
774 #[cfg(feature = "chumsky")]
775 #[test]
776 fn test_parse_attachment_point_bottom_left() {
777 assert_eq!(
778 attachment_point_parser().parse("Bottom Left").into_result(),
779 Ok(AttachmentPoint::Hud(HudAttachmentPoint::BottomLeft)),
780 );
781 }
782
783 #[cfg(feature = "chumsky")]
784 #[test]
785 fn test_parse_attachment_point_bottom() {
786 assert_eq!(
787 attachment_point_parser().parse("Bottom").into_result(),
788 Ok(AttachmentPoint::Hud(HudAttachmentPoint::Bottom)),
789 );
790 }
791
792 #[cfg(feature = "chumsky")]
793 #[test]
794 fn test_parse_attachment_point_bottom_right() {
795 assert_eq!(
796 attachment_point_parser()
797 .parse("Bottom Right")
798 .into_result(),
799 Ok(AttachmentPoint::Hud(HudAttachmentPoint::BottomRight)),
800 );
801 }
802}