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}