Skip to main content

rkg_utils/header/mii/
birthday.rs

1use std::convert::Infallible;
2
3use crate::byte_handler::{ByteHandlerError, FromByteHandler};
4
5/// Errors that can occur while constructing a [`Birthday`].
6#[derive(thiserror::Error, Debug)]
7pub enum BirthdayError {
8    /// The month value is outside the valid range (1–12).
9    #[error("Month is invalid")]
10    MonthInvalid,
11    /// The day value is zero when a month is set, or exceeds the maximum
12    /// number of days for the given month.
13    #[error("Day is invalid")]
14    DayInvalid,
15    /// A `ByteHandler` operation failed.
16    #[error("ByteHandler Error: {0}")]
17    ByteHandlerError(#[from] ByteHandlerError),
18    /// Infallible conversion error; cannot occur at runtime.
19    #[error("")]
20    Infallible(#[from] Infallible),
21}
22
23/// Represents the birthday component of a Mii, storing an optional month and day.
24///
25/// Both fields are `None` when the Mii's birthday is not set (i.e. month byte is 0).
26/// A month without a day is not a valid state and will be rejected by [`Birthday::new`].
27#[derive(Clone, Copy)]
28pub struct Birthday {
29    /// The month of the birthday (1–12), or `None` if not set.
30    month: Option<u8>,
31    /// The day of the birthday, or `None` if not set.
32    day: Option<u8>,
33}
34
35impl Birthday {
36    /// Creates a new [`Birthday`] from a raw month and day value.
37    ///
38    /// A month of `0` is treated as "not set" and produces a [`Birthday`] where
39    /// both `month` and `day` are `None`. Any other month value must be paired
40    /// with a non-zero day that is valid for that month.
41    ///
42    /// # Arguments
43    ///
44    /// * `month` - The month value (0 = not set, 1–12 = January–December).
45    /// * `day` - The day value (must be non-zero and valid for the given month).
46    ///
47    /// # Errors
48    ///
49    /// Returns [`BirthdayError::DayInvalid`] if:
50    /// - A non-zero month is provided but `day` is `0`.
51    /// - The day exceeds the maximum for the given month (31, 30, or 29 for February).
52    ///
53    /// Returns [`BirthdayError::MonthInvalid`] if `month` is greater than 12.
54    pub fn new(month: u8, day: u8) -> Result<Self, BirthdayError> {
55        match month {
56            0 => Ok(Self {
57                month: None,
58                day: None,
59            }),
60            _month if day == 0 => Err(BirthdayError::DayInvalid), // Birthday with only a month is not possible
61            1 | 3 | 5 | 7 | 8 | 10 | 12 if day > 31 => Err(BirthdayError::DayInvalid),
62            4 | 6 | 9 | 11 if day > 30 => Err(BirthdayError::DayInvalid),
63            2 if day > 29 => Err(BirthdayError::DayInvalid),
64            1..=12 => Ok(Self {
65                month: Some(month),
66                day: Some(day),
67            }),
68            _ => Err(BirthdayError::MonthInvalid),
69        }
70    }
71
72    /// Returns the month component of the birthday, or `None` if not set.
73    pub fn month(&self) -> Option<u8> {
74        self.month
75    }
76
77    /// Returns the day component of the birthday, or `None` if not set.
78    pub fn day(&self) -> Option<u8> {
79        self.day
80    }
81}
82
83/// Deserializes a [`Birthday`] from a `ByteHandler`.
84///
85/// The handler is shifted right by 2 bits before extracting the month from the
86/// lower nibble of the first byte and the day from the upper 5 bits of the second byte.
87impl FromByteHandler for Birthday {
88    type Err = BirthdayError;
89    fn from_byte_handler<T>(handler: T) -> Result<Self, Self::Err>
90    where
91        T: TryInto<crate::byte_handler::ByteHandler>,
92        Self::Err: From<T::Error>,
93    {
94        let mut handler = handler.try_into()?;
95        handler.shift_right(2);
96        Self::new(handler.copy_byte(0) & 0x0F, handler.copy_byte(1) >> 3)
97    }
98}