1use icu_calendar::{Date, cal::Hebrew};
2
3use crate::{
4 calendar::HebrewHolidayCalendar,
5 limudim::{
6 Limud,
7 interval::Interval,
8 limud::{CycleFinder, InternalLimud},
9 },
10};
11
12const DEFAULT_UNITS: [u8; 30] = [
15 9, 17, 22, 28, 34, 38, 43, 48, 54, 59, 65, 68, 71, 76, 78, 82, 87, 89, 96, 103, 105, 107, 112, 118, 119, 119, 134,
16 139, 144, 150,
17];
18
19#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
21#[cfg_attr(feature = "defmt", derive(defmt::Format))]
22#[allow(missing_docs)]
23pub enum TehillimUnit {
24 Psalms { start: u8, end: u8 },
26 PsalmVerses {
28 psalm: u8,
29 start_verse: u16,
30 end_verse: u16,
31 },
32}
33
34#[derive(Default)]
35pub struct TehillimMonthly;
37
38fn find_monthly_cycle(date: Date<Hebrew>) -> Option<(Date<Hebrew>, Date<Hebrew>)> {
40 let year = date.year().extended_year();
41 let month = date.input_month();
42
43 let start = Date::try_new_hebrew_v2(year, month, 1).ok()?;
45
46 let days_in_month = date.days_in_month();
48 let end = Date::try_new_hebrew_v2(year, month, days_in_month).ok()?;
49
50 Some((start, end))
51}
52
53impl InternalLimud<TehillimUnit> for TehillimMonthly {
54 fn cycle_finder(&self) -> CycleFinder {
55 CycleFinder::Perpetual(find_monthly_cycle)
56 }
57
58 fn unit_for_interval(&self, interval: &Interval, _limud_date: &Date<Hebrew>) -> Option<TehillimUnit> {
59 let iteration = interval.iteration;
60
61 if iteration == 25 {
63 return Some(TehillimUnit::PsalmVerses {
64 psalm: 119,
65 start_verse: 1,
66 end_verse: 96,
67 });
68 }
69
70 if iteration == 26 {
71 return Some(TehillimUnit::PsalmVerses {
72 psalm: 119,
73 start_verse: 97,
74 end_verse: 176,
75 });
76 }
77
78 let (start, mut stop) = if iteration == 1 {
80 (1, DEFAULT_UNITS[0])
81 } else {
82 let prev_end = DEFAULT_UNITS[(iteration - 2) as usize];
83 let curr_end = DEFAULT_UNITS[(iteration - 1) as usize];
84 (prev_end + 1, curr_end)
85 };
86
87 let day = interval.end_date.day_of_month().0;
89 let days_in_month = interval.end_date.days_in_month();
90 if day == 29 && days_in_month == 29 && iteration < 30 {
91 stop = DEFAULT_UNITS[iteration as usize];
92 }
93
94 Some(TehillimUnit::Psalms { start, end: stop })
95 }
96}
97impl Limud<TehillimUnit> for TehillimMonthly {}
98
99#[cfg(test)]
100#[allow(clippy::expect_used, clippy::unwrap_used)]
101mod tests {
102 use crate::calendar::month::{SHEVAT, TEVET};
103
104 use super::*;
105
106 #[test]
107 fn tehillim_monthly_simple_date() {
108 let test_date = Date::try_new_hebrew_v2(5778, TEVET, 8).unwrap();
110 let limud = TehillimMonthly.limud(test_date).expect("limud exists");
111 assert_eq!(limud, TehillimUnit::Psalms { start: 44, end: 48 });
113 }
114
115 #[test]
116 fn tehillim_monthly_beginning_of_month() {
117 let test_date = Date::try_new_hebrew_v2(5778, TEVET, 1).unwrap();
119 let limud = TehillimMonthly.limud(test_date).expect("limud exists");
120 assert_eq!(limud, TehillimUnit::Psalms { start: 1, end: 9 });
122 }
123
124 #[test]
125 fn tehillim_monthly_end_of_short_month() {
126 let test_date = Date::try_new_hebrew_v2(5778, TEVET, 29).unwrap();
128 let limud = TehillimMonthly.limud(test_date).expect("limud exists");
129 assert_eq!(limud, TehillimUnit::Psalms { start: 140, end: 150 });
131 }
132
133 #[test]
134 fn tehillim_monthly_end_of_long_month() {
135 let test_date = Date::try_new_hebrew_v2(5778, SHEVAT, 30).unwrap();
137 let limud = TehillimMonthly.limud(test_date).expect("limud exists");
138 assert_eq!(limud, TehillimUnit::Psalms { start: 145, end: 150 });
140 }
141
142 #[test]
143 fn tehillim_monthly_29th_day_of_long_month() {
144 let test_date = Date::try_new_hebrew_v2(5778, SHEVAT, 29).unwrap();
146 let limud = TehillimMonthly.limud(test_date).expect("limud exists");
147 assert_eq!(limud, TehillimUnit::Psalms { start: 140, end: 144 });
149 }
150
151 #[test]
152 fn tehillim_monthly_day_25_special_case() {
153 let test_date = Date::try_new_hebrew_v2(5778, SHEVAT, 25).unwrap();
155 let limud = TehillimMonthly.limud(test_date).expect("limud exists");
156 assert_eq!(
158 limud,
159 TehillimUnit::PsalmVerses {
160 psalm: 119,
161 start_verse: 1,
162 end_verse: 96
163 }
164 );
165 }
166
167 #[test]
168 fn tehillim_monthly_day_26_special_case() {
169 let test_date = Date::try_new_hebrew_v2(5778, SHEVAT, 26).unwrap();
171 let limud = TehillimMonthly.limud(test_date).expect("limud exists");
172 assert_eq!(
174 limud,
175 TehillimUnit::PsalmVerses {
176 psalm: 119,
177 start_verse: 97,
178 end_verse: 176
179 }
180 );
181 }
182}