sl_types/
attachment.rs

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