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