Skip to main content

klirr_core/models/
day.rs

1use crate::prelude::*;
2
3/// The day of the month, e.g. 1 for the first day, 31 for the last day of a month.
4#[derive(Clone, Copy, Debug, PartialEq, Eq, Hash, Display, Serialize, Deserialize, Deref)]
5pub struct Day(u8);
6
7impl HasSample for Day {
8    fn sample() -> Self {
9        Self(1)
10    }
11    fn sample_other() -> Self {
12        Self(15)
13    }
14}
15
16impl std::str::FromStr for Day {
17    type Err = crate::prelude::Error;
18
19    /// Parses a day from a string, e.g. "1" for the first day, "31" for the last day of a month.
20    /// # Errors
21    /// Returns an error if the string is not a valid day or if it is out of range.
22    /// # Examples
23    /// ```
24    /// extern crate klirr_core;
25    /// use klirr_core::prelude::*;
26    /// let day: Day = "15".parse().unwrap();
27    /// assert_eq!(*day, 15);
28    /// ```
29    fn from_str(s: &str) -> Result<Self, Self::Err> {
30        let day = s.parse::<i32>().map_err(|_| Error::InvalidDayFromString {
31            invalid_string: s.to_string(),
32            reason: "Invalid day format".to_string(),
33        })?;
34        Self::try_from(day)
35    }
36}
37
38impl TryFrom<i32> for Day {
39    type Error = crate::prelude::Error;
40    fn try_from(day: i32) -> Result<Self> {
41        if !(1..=31).contains(&day) {
42            return Err(Error::InvalidDay {
43                day,
44                reason: "Day must be between 1 and 31".to_string(),
45            });
46        }
47        Ok(Self(day as u8))
48    }
49}
50
51impl TryFrom<u8> for Day {
52    type Error = crate::prelude::Error;
53    fn try_from(day: u8) -> Result<Self> {
54        Self::try_from(day as i32)
55    }
56}
57
58impl TryFrom<u32> for Day {
59    type Error = crate::prelude::Error;
60    fn try_from(day: u32) -> Result<Self> {
61        Self::try_from(day as i32)
62    }
63}
64
65#[cfg(test)]
66mod tests {
67    use super::*;
68    use test_log::test;
69
70    type Sut = Day;
71
72    #[test]
73    fn equality() {
74        assert_eq!(Sut::sample(), Sut::sample());
75        assert_eq!(Sut::sample_other(), Sut::sample_other());
76    }
77
78    #[test]
79    fn inequality() {
80        assert_ne!(Sut::sample(), Sut::sample_other());
81    }
82
83    #[test]
84    fn test_day_conversion() {
85        assert_eq!(Sut::try_from(1).unwrap(), Day(1));
86        assert_eq!(Sut::try_from(15).unwrap(), Day(15));
87        assert_eq!(Sut::try_from(31).unwrap(), Day(31));
88        assert!(Sut::try_from(0).is_err());
89        assert!(Sut::try_from(32).is_err());
90    }
91
92    #[test]
93    fn test_day_from_str() {
94        let day: Sut = "15".parse().unwrap();
95        assert_eq!(day, Day(15));
96    }
97
98    #[test]
99    fn test_day_from_invalid_all_reasons() {
100        let invalid_strings = [
101            "0",    // Invalid day (0)
102            "32",   // Invalid day (greater than 31)
103            "-1",   // Invalid day (negative)
104            "abc",  // Invalid format (not a number)
105            "15.5", // Invalid format (decimal)
106        ];
107
108        for &s in &invalid_strings {
109            assert!(Sut::from_str(s).is_err(), "Expected error for input: {}", s);
110        }
111    }
112}