Skip to main content

rkg_utils/header/mii/
facial_hair.rs

1use std::convert::Infallible;
2
3use crate::{
4    byte_handler::{ByteHandlerError, FromByteHandler},
5    header::mii::hair::HairColor,
6};
7
8/// Represents the facial hair customization options of a Mii,
9/// including beard type, mustache type, color, and mustache size and position.
10#[derive(Clone, Copy)]
11pub struct FacialHair {
12    /// Beard shape/style.
13    beard_type: BeardType,
14    /// Mustache shape/style.
15    mustache_type: MustacheType,
16    /// Facial hair color, shared with the hair color palette.
17    color: HairColor,
18    /// Mustache size (0–8).
19    mustache_size: u8,
20    /// Vertical position of the mustache (0–16).
21    mustache_y: u8,
22}
23
24impl FacialHair {
25    /// Creates a new [`FacialHair`] from its individual components.
26    ///
27    /// # Arguments
28    ///
29    /// * `beard_type` - Beard shape/style.
30    /// * `mustache_type` - Mustache shape/style.
31    /// * `color` - Facial hair color from the [`HairColor`] palette.
32    /// * `mustache_size` - Mustache size (0–8).
33    /// * `mustache_y` - Vertical position of the mustache (0–16).
34    ///
35    /// # Errors
36    ///
37    /// Returns [`FacialHairError::SizeInvalid`] if `mustache_size` exceeds 8.
38    /// Returns [`FacialHairError::YInvalid`] if `mustache_y` exceeds 16.
39    pub fn new(
40        beard_type: BeardType,
41        mustache_type: MustacheType,
42        color: HairColor,
43        mustache_size: u8,
44        mustache_y: u8,
45    ) -> Result<Self, FacialHairError> {
46        if mustache_size > 8 {
47            return Err(FacialHairError::SizeInvalid);
48        }
49        if mustache_y > 16 {
50            return Err(FacialHairError::YInvalid);
51        }
52
53        Ok(Self {
54            beard_type,
55            mustache_type,
56            color,
57            mustache_size,
58            mustache_y,
59        })
60    }
61
62    /// Returns the beard shape/style.
63    pub fn beard_type(&self) -> BeardType {
64        self.beard_type
65    }
66
67    /// Returns the mustache shape/style.
68    pub fn mustache_type(&self) -> MustacheType {
69        self.mustache_type
70    }
71
72    /// Returns the facial hair color.
73    pub fn color(&self) -> HairColor {
74        self.color
75    }
76
77    /// Returns the mustache size (0–8).
78    pub fn mustache_size(&self) -> u8 {
79        self.mustache_size
80    }
81
82    /// Returns the vertical position of the mustache (0–16).
83    pub fn mustache_y(&self) -> u8 {
84        self.mustache_y
85    }
86
87    /// Sets the beard shape/style.
88    pub fn set_beard_type(&mut self, beard_type: BeardType) {
89        self.beard_type = beard_type;
90    }
91
92    /// Sets the mustache shape/style.
93    pub fn set_mustache_type(&mut self, mustache_type: MustacheType) {
94        self.mustache_type = mustache_type;
95    }
96
97    /// Sets the facial hair color.
98    pub fn set_color(&mut self, color: HairColor) {
99        self.color = color;
100    }
101
102    /// Sets the mustache size.
103    ///
104    /// # Errors
105    ///
106    /// Returns [`FacialHairError::SizeInvalid`] if `mustache_size` exceeds 8.
107    pub fn set_mustache_size(&mut self, mustache_size: u8) -> Result<(), FacialHairError> {
108        if mustache_size > 8 {
109            return Err(FacialHairError::SizeInvalid);
110        }
111        self.mustache_size = mustache_size;
112        Ok(())
113    }
114
115    /// Sets the vertical position of the mustache.
116    ///
117    /// # Errors
118    ///
119    /// Returns [`FacialHairError::YInvalid`] if `mustache_y` exceeds 16.
120    pub fn set_mustache_y(&mut self, mustache_y: u8) -> Result<(), FacialHairError> {
121        if mustache_y > 16 {
122            return Err(FacialHairError::YInvalid);
123        }
124        self.mustache_y = mustache_y;
125        Ok(())
126    }
127}
128
129/// Deserializes [`FacialHair`] from a `ByteHandler`.
130///
131/// Extracts and unpacks the mustache vertical position, mustache size, facial hair color,
132/// mustache type, and beard type from the packed Mii binary format using bit shifts and masks.
133impl FromByteHandler for FacialHair {
134    type Err = FacialHairError;
135    fn from_byte_handler<T>(handler: T) -> Result<Self, Self::Err>
136    where
137        T: TryInto<crate::byte_handler::ByteHandler>,
138        Self::Err: From<T::Error>,
139    {
140        let mut handler = handler.try_into()?;
141        let mustache_y = handler.copy_byte(1) & 0x1F;
142        handler.shift_right(1);
143
144        let mustache_size = handler.copy_byte(1) >> 4;
145        let color = HairColor::try_from(handler.copy_byte(0) & 0x07)
146            .map_err(|_| FacialHairError::ColorInvalid)?;
147        let mustache_type = MustacheType::try_from(handler.copy_byte(0) >> 5)
148            .map_err(|_| FacialHairError::MustacheTypeInvalid)?;
149        let beard_type = BeardType::try_from((handler.copy_byte(0) >> 3) & 0x03)
150            .map_err(|_| FacialHairError::BeardTypeInvalid)?;
151
152        Self::new(beard_type, mustache_type, color, mustache_size, mustache_y)
153    }
154}
155
156/// Errors that can occur while constructing or deserializing [`FacialHair`].
157#[derive(thiserror::Error, Debug)]
158pub enum FacialHairError {
159    /// The beard type byte did not map to a known [`BeardType`] variant.
160    #[error("Beard Type is invalid")]
161    BeardTypeInvalid,
162    /// The mustache type byte did not map to a known [`MustacheType`] variant.
163    #[error("Mustache Type is invalid")]
164    MustacheTypeInvalid,
165    /// The facial hair color byte did not map to a known [`HairColor`] variant.
166    #[error("Color is invalid")]
167    ColorInvalid,
168    /// The mustache size exceeds the maximum of 8.
169    #[error("Size is invalid")]
170    SizeInvalid,
171    /// The mustache vertical position exceeds the maximum of 16.
172    #[error("Y position is invalid")]
173    YInvalid,
174    /// A `ByteHandler` operation failed.
175    #[error("ByteHandler Error: {0}")]
176    ByteHandlerError(#[from] ByteHandlerError),
177    /// Infallible conversion error; cannot occur at runtime.
178    #[error("")]
179    Infallible(#[from] Infallible),
180}
181
182/// Beard styles available in the Mii editor.
183#[derive(Clone, Copy, PartialEq, Debug)]
184pub enum BeardType {
185    /// No beard.
186    None,
187    /// Short goatee.
188    Goatee,
189    /// Long goatee.
190    GoateeLong,
191    /// Long lion's mane style beard.
192    LionsManeLong,
193}
194
195/// Converts a raw byte value from the Mii data format into a [`BeardType`].
196///
197/// Returns `Err(())` if the byte does not correspond to any known beard type.
198impl TryFrom<u8> for BeardType {
199    type Error = ();
200    fn try_from(value: u8) -> Result<Self, Self::Error> {
201        match value {
202            0 => Ok(Self::None),
203            1 => Ok(Self::Goatee),
204            2 => Ok(Self::GoateeLong),
205            3 => Ok(Self::LionsManeLong),
206            _ => Err(()),
207        }
208    }
209}
210
211/// Converts a [`BeardType`] into its raw byte representation for the Mii data format.
212impl From<BeardType> for u8 {
213    fn from(value: BeardType) -> Self {
214        match value {
215            BeardType::None => 0,
216            BeardType::Goatee => 1,
217            BeardType::GoateeLong => 2,
218            BeardType::LionsManeLong => 3,
219        }
220    }
221}
222
223/// Mustache styles available in the Mii editor.
224#[derive(Clone, Copy, PartialEq, Debug)]
225pub enum MustacheType {
226    /// No mustache.
227    None,
228    /// Full, drooping walrus mustache.
229    Walrus,
230    /// Thin pencil mustache.
231    Pencil,
232    /// Horseshoe-shaped mustache.
233    Horseshoe,
234}
235
236/// Converts a raw byte value from the Mii data format into a [`MustacheType`].
237///
238/// Returns `Err(())` if the byte does not correspond to any known mustache type.
239impl TryFrom<u8> for MustacheType {
240    type Error = ();
241    fn try_from(value: u8) -> Result<Self, Self::Error> {
242        match value {
243            0 => Ok(Self::None),
244            1 => Ok(Self::Walrus),
245            2 => Ok(Self::Pencil),
246            3 => Ok(Self::Horseshoe),
247            _ => Err(()),
248        }
249    }
250}
251
252/// Converts a [`MustacheType`] into its raw byte representation for the Mii data format.
253impl From<MustacheType> for u8 {
254    fn from(value: MustacheType) -> Self {
255        match value {
256            MustacheType::None => 0,
257            MustacheType::Walrus => 1,
258            MustacheType::Pencil => 2,
259            MustacheType::Horseshoe => 3,
260        }
261    }
262}