1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
use crate::{BadiDateError, BadiDateLike, BahaiHolyDay};

/// Provides methods to get the current, previous, and next Bahá’í holy day
pub trait HolyDayProviding: BadiDateLike {
    /// Gets the holy day for `self.day` if it is a holy day
    fn holy_day(&self) -> Option<BahaiHolyDay> {
        BahaiHolyDay::holy_days_for_year(self.year())
            .get(&self.day_of_year())
            .cloned()
    }

    /// Gets the next holy day, if within the supported date range
    fn next_holy_day(&self) -> Result<Self, BadiDateError> {
        for year in [self.year(), self.year() + 1] {
            let after_day = if year == self.year() {
                self.day_of_year()
            } else {
                0
            };
            println!("next_holy_day: year = {}", year);
            if let Some((day_of_year, _holy_day)) = BahaiHolyDay::holy_days_for_year(year)
                .into_iter()
                .filter(|(day_of_year, _)| *day_of_year > after_day)
                .next()
            {
                println!(
                    "next_holy_day: day_of_year, _holy_day = {}, {:?}",
                    day_of_year, _holy_day
                );

                return self.with_year_and_doy(year, day_of_year);
            }
        }
        Err(BadiDateError::DateNotSupported)
    }

    /// Gets the previous holy day, if within the supported date range
    fn previous_holy_day(&self) -> Result<Self, BadiDateError> {
        for year in [self.year(), self.year() - 1] {
            let before_day = if year == self.year() {
                self.day_of_year()
            } else {
                366
            };
            if let Some((day_of_year, _holy_day)) = BahaiHolyDay::holy_days_for_year(year)
                .into_iter()
                .filter(|(day_of_year, _)| *day_of_year < before_day)
                .last()
            {
                return self.with_year_and_doy(year, day_of_year);
            }
        }
        Err(BadiDateError::DateNotSupported)
    }
}

#[cfg(test)]
mod tests {
    use crate::{BadiDate, BadiDateOps, BadiMonth, BahaiHolyDay, HolyDayProviding};

    #[test]
    fn add_subtract_next_previous() {
        // 2 Nur 181 B.E. / 5 June 2024
        let badi = BadiDate::new(181, BadiMonth::Month(5), 2).unwrap();
        let next_holy_day: BadiDate = badi.next_holy_day().unwrap();
        assert_eq!(
            next_holy_day,
            // Martyrdom of the Báb
            BadiDate::new(181, BadiMonth::Month(6), 17).unwrap(),
        );
        assert_eq!(
            next_holy_day.holy_day(),
            Some(BahaiHolyDay::MartyrdomOfTheBab)
        );
        assert_eq!(next_holy_day.holy_day().unwrap().work_suspended(), true);
        let next_holy_day: BadiDate = next_holy_day.next_holy_day().unwrap();
        assert_eq!(
            next_holy_day,
            // Birth of the Báb
            BadiDate::new(181, BadiMonth::Month(12), 19).unwrap(),
        );
        assert_eq!(next_holy_day.holy_day(), Some(BahaiHolyDay::BirthOfTheBab));
        let next_holy_day: BadiDate = next_holy_day.next_holy_day().unwrap();
        assert_eq!(
            next_holy_day,
            // Birth of Bahá’u’lláh
            BadiDate::new(181, BadiMonth::Month(13), 1).unwrap(),
        );
        assert_eq!(
            next_holy_day.holy_day(),
            Some(BahaiHolyDay::BirthOfBahaullah)
        );
        let next_day = next_holy_day.next_day();
        assert_eq!(next_day.holy_day(), None);
        let next_holy_day: BadiDate = next_holy_day
            .next_holy_day()
            .unwrap()
            .next_holy_day()
            .unwrap();
        assert_eq!(
            next_holy_day,
            // Ascension of ‘Abdu’l-Bahá
            BadiDate::new(181, BadiMonth::Month(14), 6).unwrap(),
        );
        let prev_holy_day = badi.previous_holy_day().unwrap();
        assert_eq!(
            prev_holy_day,
            // Ascension of Bahá’u’lláh
            BadiDate::new(181, BadiMonth::Month(4), 13).unwrap(),
        );
        let prev_holy_day = prev_holy_day.previous_holy_day().unwrap();
        assert_eq!(
            prev_holy_day,
            // Declaration of the Báb
            BadiDate::new(181, BadiMonth::Month(4), 8).unwrap(),
        );

        let badi = BadiDate::new(181, BadiMonth::AyyamIHa, 2).unwrap();
        let next_holy_day: BadiDate = badi.next_holy_day().unwrap();
        assert_eq!(
            next_holy_day,
            // Naw-Rúz
            BadiDate::new(182, BadiMonth::Month(1), 1).unwrap(),
        );
        assert_eq!(next_holy_day.holy_day(), Some(BahaiHolyDay::NawRuz),);

        let previous_holy_day: BadiDate = next_holy_day.previous_holy_day().unwrap();
        assert_eq!(
            previous_holy_day,
            // Ascension of ‘Abdu’l-Bahá
            BadiDate::new(181, BadiMonth::Month(14), 6).unwrap(),
        );
        assert_eq!(
            previous_holy_day.holy_day(),
            Some(BahaiHolyDay::AscensionOfAbdulBaha),
        );
        assert_eq!(
            previous_holy_day.holy_day().unwrap().work_suspended(),
            false,
        );
    }
}