commonlibsse_ng\re\c\Calendar/
day.rs

1/// Represents the days of the week.
2#[repr(u32)]
3#[derive(Debug, Default, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
4pub enum Week {
5    #[default]
6    Sundas,
7    Morndas,
8    Tirdas,
9    Middas,
10    Turdas,
11    Fredas,
12    Loredas,
13    // Total, // unused
14}
15
16impl Week {
17    /// Converts the `Week` enum into a string representation.
18    ///
19    /// # Example
20    /// ```
21    /// # use commonlibsse_ng::re::Calendar::Week;
22    /// let day = Week::Sundas;
23    /// assert_eq!(day.as_str(), "Sundas");
24    /// ```
25    #[inline]
26    pub const fn as_str(&self) -> &'static str {
27        match *self {
28            Self::Sundas => "Sundas",
29            Self::Morndas => "Morndas",
30            Self::Tirdas => "Tirdas",
31            Self::Middas => "Middas",
32            Self::Turdas => "Turdas",
33            Self::Fredas => "Fredas",
34            Self::Loredas => "Loredas",
35        }
36    }
37
38    /// Converts a `u32` value into a corresponding `Week` variant.
39    ///
40    /// Returns `None` if the value is outside the range of valid days (0-6).
41    ///
42    /// # Example
43    /// ```
44    /// # use commonlibsse_ng::re::Calendar::Week;
45    /// let day = Week::from_u32(2);
46    /// assert_eq!(day, Some(Week::Tirdas));
47    /// ```
48    ///
49    #[inline]
50    pub const fn from_u32(v: u32) -> Option<Self> {
51        Some(match v {
52            0 => Self::Sundas,
53            1 => Self::Morndas,
54            2 => Self::Tirdas,
55            3 => Self::Middas,
56            4 => Self::Turdas,
57            5 => Self::Fredas,
58            6 => Self::Loredas,
59            _ => return None,
60        })
61    }
62}
63
64/// Represents a day in a 0-based game.
65///
66/// This usually takes the range `0.0..=30.0` range.
67#[derive(Debug, Default, Clone, Copy, PartialEq)]
68#[repr(transparent)]
69pub struct GameDay(pub f32);
70
71impl GameDay {
72    /// The default `GameDay` value (0.0) at compile time.
73    pub const DEFAULT: Self = Self(0.0);
74
75    /// Creates a new `GameDay` instance with the specified value.
76    ///
77    /// # Example
78    /// ```
79    /// # use commonlibsse_ng::re::Calendar::GameDay;
80    /// let game_day = GameDay::new(5.0);
81    /// assert_eq!(game_day.0, 5.0);
82    /// ```
83    #[inline]
84    pub const fn new(value: f32) -> Self {
85        Self(value)
86    }
87
88    /// Returns the day of the week (0-6), 0 based value.
89    ///
90    /// # Example
91    /// ```
92    /// # use commonlibsse_ng::re::Calendar::GameDay;
93    /// let game_day = GameDay::new(3.0);
94    /// assert_eq!(game_day.day_of_week(), 3);
95    /// ```
96    #[inline]
97    pub const fn day_of_week(&self) -> u32 {
98        (self.0 as u32) % 7
99    }
100
101    /// Clamps the day value based on the month's maximum days.
102    ///
103    /// When month is in the range of 1 to 12, a valid value is returned.
104    ///
105    /// # Example
106    /// ```
107    /// use commonlibsse_ng::re::Calendar::GameDay;
108    /// let game_day = GameDay::new(32.0);
109    /// assert_eq!(game_day.to_clamp_day(2), 28); // Sun's Dawn (28 days)
110    ///
111    /// assert_eq!(game_day.to_clamp_day(0), 31); // Underflow (Fallback to 31 days)
112    /// assert_eq!(game_day.to_clamp_day(12), 31); // Overflow (Fallback to 31 days)
113    /// assert_eq!(game_day.to_clamp_day(300), 31); // Overflow (Fallback to 31 days)
114    /// ```
115    #[inline]
116    pub const fn to_clamp_day(self, month: u32) -> u32 {
117        /// Days in each month
118        pub const DAYS_IN_MONTH: [u8; 12] = [
119            31, // Morning Star
120            28, // Sun's Dawn
121            31, // First Seed
122            30, // Rain's Hand
123            31, // Second Seed
124            30, // Midyear
125            31, // Sun's Height
126            31, // Last Seed
127            30, // Hearthfire
128            31, // Frostfall
129            30, // Sun's Dusk
130            31, // Evening Star
131        ];
132        let max_days = match month {
133            1..=12 => DAYS_IN_MONTH[(month - 1) as usize] as u32,
134            _ => 31,
135        };
136        let n = self.0 as u32;
137        if n < max_days { n } else { max_days }
138    }
139
140    /// Returns the ordinal suffix for the day (e.g., `st`, `nd`, `rd`, `th`).
141    ///
142    /// # Example
143    /// ```
144    /// use commonlibsse_ng::re::Calendar::GameDay;
145    /// let game_day = GameDay::new(21.0);
146    /// assert_eq!(game_day.ordinal_suffix(), "st");
147    /// ```
148    #[inline]
149    pub const fn ordinal_suffix(&self) -> &'static str {
150        match self.0 as i32 {
151            1 | 21 | 31 => "st",
152            2 | 22 => "nd",
153            3 | 23 => "rd",
154            _ => "th",
155        }
156    }
157
158    /// Converts `GameDay` into a `Week` enum.
159    ///
160    /// Returns `None` if the `GameDay` value is out of range (greater than 7.0).
161    ///
162    /// # Example
163    /// ```
164    /// use commonlibsse_ng::re::Calendar::{GameDay, Week};
165    /// let game_day = GameDay::new(1.0);
166    /// assert_eq!(game_day.to_week(), Some(Week::Morndas));
167    /// ```
168    #[inline]
169    pub const fn to_week(self) -> Option<Week> {
170        Some(match self.0 as u32 {
171            0 => Week::Sundas,
172            1 => Week::Morndas,
173            2 => Week::Tirdas,
174            3 => Week::Middas,
175            4 => Week::Turdas,
176            5 => Week::Fredas,
177            6 => Week::Loredas,
178            _ => return None,
179        })
180    }
181}
182
183#[cfg(test)]
184mod tests {
185    use super::*;
186
187    #[test]
188    fn test_week_from_u32() {
189        assert_eq!(Week::from_u32(0), Some(Week::Sundas));
190        assert_eq!(Week::from_u32(1), Some(Week::Morndas));
191        assert_eq!(Week::from_u32(6), Some(Week::Loredas));
192        assert_eq!(Week::from_u32(7), None);
193        assert_eq!(Week::from_u32(100), None);
194    }
195
196    #[test]
197    fn test_game_day_to_week() {
198        assert_eq!(GameDay::new(0.0).to_week(), Some(Week::Sundas));
199        assert_eq!(GameDay::new(3.0).to_week(), Some(Week::Middas));
200        assert_eq!(GameDay::new(6.0).to_week(), Some(Week::Loredas));
201        assert_eq!(GameDay::new(7.0).to_week(), None); // Out of valid range
202    }
203
204    #[test]
205    fn test_game_day_clamp() {
206        assert_eq!(GameDay::new(32.0).to_clamp_day(2), 28); // February (28 days)
207        assert_eq!(GameDay::new(32.0).to_clamp_day(4), 30); // April (30 days)
208        assert_eq!(GameDay::new(32.0).to_clamp_day(12), 31); // December (31 days)
209    }
210
211    #[test]
212    fn test_ordinal_suffix() {
213        assert_eq!(GameDay::new(1.0).ordinal_suffix(), "st");
214        assert_eq!(GameDay::new(2.0).ordinal_suffix(), "nd");
215        assert_eq!(GameDay::new(3.0).ordinal_suffix(), "rd");
216        assert_eq!(GameDay::new(4.0).ordinal_suffix(), "th");
217    }
218}