Skip to main content

rkg_utils/header/mii/
glasses.rs

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